Monthly Archives: May 2013

Making Sencha Touch + PhoneGap iOS Apps Available to Beta Testers

This is part 4 of a 4 part series.

After you’ve successfully compiled your app in XCode, you’ll want to make it available to beta testers. There are three methods that you can use to accomplish this.

1) Use an Ad-Hoc Distribution Method
2) Use an Ad-Hoc Distribution Method in combination with the TestFlight service to facilitate “over-the-air” (OTA) installs
3) Register for Apple’s iOS Enterprise Developer Program and distribute your app “over-the-air”

Using the Ad-Hoc Distribution Method

The Ad-Hoc distribution method enables testers to install your app on up to 100 devices.  You’ll need to use this technique if you aren’t a member of the iOS Enterprise Developer Program ($299 annual fee). Users will need to email you their device’s UDID so that you can enter them into the provisioning portal.

Using this distribution method involves completing the following steps:

  1. Acquire the UDID of the beta-tester’s device
  2. Registering devices with the Provisioning Portal
  3. Create a distribution certificate, if none exists
  4. Create an Ad-Hoc Distribution Provisioning Profile
  5. Build an App Archive
  6. Code-sign the app using the Ad-Hoc Provisioning Profile
  7. Post the app to a webserver for download.

Acquiring a Test Device’s UDID

The steps that a user must perform to retrieve a device’s UDID are as follows:

  1. Tether the device to their laptop/desktop computer. iTunes should automatically launch.
  2. Click on the Devices button in iTunes (illustrated by Step 1, below)
  3. Click on the tethered device (illustrated by Step 2, below)
  4. Click on the Serial Number (step 3) – note that this is actually an invisible button. And no, I’m not making this up. The serial number will magically transform into the device’s UDID.
  5. Right-click on the UDID and select Copy Identifier (UDID). This will place the UDID onto the user’s clipboard.
  6. Paste the UDID into an email and send to the app developer.

itunes

Registering a Device with the Provisioning Portal

Once you, as the developer, have the beta-tester’s device’s ID, you should perform the following steps:

  1. Log into the provisioning portal
  2. Click on the Certificates, Identifiers & Profiles link
  3. Click on the Devices link
  4. Click on the [+] to register a test device. Enter the a description of your tester’s device, and paste the UDID that they sent to you in an email.

a3

Creating a Distribution Certificate

If you haven’t already done so, you’ll need to create a distribution certificate  as described by the following steps:

  1. Click on Certificates > Distribution
  2. Click the [+] to add a new certificate
  3. Select Distribution > App Store and Ad Hoc
  4. Click Continue
  5. Follow the steps to create a certificate signing request (CSR). Click Continue.
  6. Select your CSR and click Generate
  7. Click the Download button to download your certificate
  8. Double-click on your downloaded certificate to install it into KeyChain Access.

Creating an Ad-Hoc Provisioning Profile

  1. Return to the provisioning portal
  2. Click on Provisioning Profiles > All
  3. Click the [+] button
  4. Click on Distribution > Ad Hoc as illustrated below:Adhoc
  5. Click Continue
  6. Select your App ID and click Continue
  7. Choose your iOS Distribution certificate and click Continue.
  8. Choose the devices that you want to use for testing your app as illustrated below:devices
  9. Click Continue
  10. Name the profile as illustrated below and click Generate.adhoctest
  11. Download the resulting .mobileprovision file and then double-click to install it into XCode Organizer

Building an App Archive

You must now compile your app into a .ipa file by completing the following steps:

  1. Open your project in XCode
  2. On the Project Summary tab, illustrated below, enter a version number for your app (typically 1.0)a4
  3. Click on the Build Settings tab
  4. As illustrated below, verify that your Code Signing Identity for app Release is set to the distribution certificate contained in your Ad Hoc Testing provisioning file.a5
  5. Choose Product > Archive from the XCode menu. After you are prompted to code sign, your archived app should appear in Organizer as illustrated below. Note: If you get a build error “Command /usr/sbin/chown failed with exit code 1”, this indicates that you have a permissions issue with your xcode project directory. You can fix this by issuing the following command:
    sudo chown -R [your osx account] [xcode project foldername]

    e.g.

    sudo chown -R stevedrucker notifications

    a6

Distributing the App

  1. In Organizer, click on the Distribute button
  2. Click Save for Enterprise or Ad-Hoc Deployment
  3. Click Next
  4. Select your Ad-Hoc Testing Code Signing Identity and click Next. Your application will be codesigned.
  5. As illustrated below, click on the Save for Enterprise Distribution checkbox and fill in the URL of where you intend to post the app.a7
  6. Click Save.
  7. This will generate a .ipa file and a .plist file.
  8. Upload the .ipa file and .plist file to your web server.
  9. Configure your web server to support the following mime types:
    .ipa   application/octet-stream
    .plist application/xml
  10. Create an HTML page that contains a link that the user can click on in order to install the app. The hyperlink should be similar to the following:
    <a href="itms-services://?action=download-manifest&url=http://training.figleaf.com/FigLeafNotifications.plist">Click here to download app</a>
    

Using a Third-Party Distribution Service

If you don’t have a webserver from which to distribute your app to testers, you can use a third-party service, such as TestFlight. TestFlight enables you to upload your ad-hoc provisioned app and generates a URL from which your testers can download and install the app. It also provides reporting metrics on who has installed the app, as well as notifies your testers whenever an update is available. It also enables you to revoke access to builds. By integrating the TestFlight API into your app, you can also quickly add customer feedback and review metrics on usage activity and crashes.

a9

In order to use TestFlight, your testers must download and install the TestFlight app. While the installation process is quite smooth, you should send a note to your testers informing them of this requirement.

Distributing to an Internal Enterprise

Clearly, the major sticking point with the aforementioned procedures is Apple’s requirement that you register the UDID of each test device. You can bypass this requirement by applying to Apple’s iOS Enterprise Developer Program. Membership in this program enables you to generate an “in-house” provisioning file that does *not* require registration of test devices.

a10

Once you have generated an In House provisioning file and installed it into XCode, you can complete the steps described previously under the heading “Distribute your App”, but use your in-house credential instead of an ad-hoc credential to code-sign the app.

Now that wasn’t too complicated, was it?

If you need help with your project or would like to be trained in the fine art of app development, please contact us at info@figleaf.com

Fire the Photons! Animating Sprites in Sencha Touch 2.2

boldlygo

Sencha Touch 2.2 contains a cross-device compatible canvas/svg drawing package that enables you to easily produce some really nice sprite-based animations.

Instantiating a Draw Component

Instantiate a draw component (xtype: ‘draw’) as illustrated below. Note that while the Ext.draw.Component class inherits from Ext.Container, docking components in a draw component directly exposes an animation bug that I reported to Sencha this morning.

Draw components are transparent by default, so you’ll probably want to set a background color on the parent container as illustrated below.

Ext.define('SpriteTest.view.Main', {
 extend: 'Ext.Container',
 xtype: 'main',
 requires: [
  'Ext.TitleBar',
  'Ext.Toolbar',
  'Ext.draw.Component',
  'SpriteTest.view.Photon' // torpedo to be added later
 ],

 config: {
  style: 'background-color: black',
  layout: 'fit'
 },

 photonTube: 0,

 initialize: function() {
  this.setItems(
   [{
     xtype: 'titlebar',
     title: 'Boldly Go...',
     docked: 'top'
    }, {
     xtype: 'draw',
     itemId: 'space'
    }, {
     xtype: 'toolbar',
     docked: 'bottom',
     layout: { pack: 'center' },
     items: [{
              xtype: 'button',
              text: 'Fire!'
            }]
     }
   ])
 }
});

Extending a Sprite Class

Sencha Touch supports the following types of sprites:

  • Arc
  • Circle
  • Composite
  • Ellipse
  • EllipticalArc
  • Image
  • Instancing
  • Path
  • Rect
  • Sector
  • Text

Sprites use a slightly different class system than the rest of Sencha Touch. You configure default values and custom properties using the syntax described in the following example:

Ext.define('SpriteTest.view.Photon', {
 extend: 'Ext.draw.sprite.Image',
 alias: 'sprite.photontorpedo',

 inheritableStatics: {
  def: {
   processors: { // define custom properties
    photonTube: 'string'
   },
   defaults: {  // configure default properties
     src: 'resources/images/photon.png',
     width: 150,
     height: 150,
     photonTube: '0'
   }
  }
 },

 constructor: function() {
   this.callParent(arguments);
   /* call custom code here */
 }
});

While other classes in Sencha Touch wrap configurable properties in a config block, sprites define their configs and defaults using very different syntax. Also, getters and setters are *not* automatically created in this construct.

Adding Sprites to a Draw Component

Technically, you don’t really add Sprites to a draw component. You actually add sprites to the “surface” that is instantiated by the draw component. The draw component’s surface is either an abstraction of an HTML5 canvas (on iOS platforms) or SVG (non-iOS platforms).

The following code snippet builds out the SpriteTest.view.Main class “Fire!” button described above.

{
 xtype: 'button',
 text: 'Fire!',
 scope: this,
 handler: function(b, e) {

  var surface = this.down('#space').getSurface();

  surface.add({
   type: 'photontorpedo',
   surface: surface,
   photonTube: this.photonTube
  });

  if (this.photonTube == 0)
   this.photonTube = 1;
  else
   this.photonTube = 0;
 }
}

Animating Sprites

You can change the appearance of a sprite in a single frame by modifying its attributes via the sprite.setAttributes() method and then subsequently calling the surface.renderFrame() method as illustrated below:

var component = new Ext.draw.Component();
Ext.Viewport.setLayout('fit');
Ext.Viewport.add(component);
var mySprite = component.getSurface().add({
    type: 'text',
    x: 50,
    y: 50,
    text: 'Fig Leaf Software Rulez',
    fontSize: 18,
    fillStyle: 'blue'
});
mySprite.setAttributes({
    x: 100,
    y: 100
});
component.getSurface().renderFrame(); // apply all changes

What is not generally understood, however, is that you can animate a sprite by modifying a sprite’s fx property, which functions as a pointer to an instance of the Ext.draw.modifier.Animation class. Here you can set a time duration (in ms) over which changes to sprite attributes will be gradually applied. You can also define an event listener that is fired once the animation has completed executing.

This technique is illustrated by the fire() method of the photon torpedo class, listed below:


Ext.define('SpriteTest.view.Photon', {
 extend: 'Ext.draw.sprite.Image',
 alias: 'sprite.photontorpedo',

 inheritableStatics: {
  def: {
   processors: {
    photonTube: 'string'
   },

   defaults: {
    src: 'resources/images/photon.png',
    width: 150,
    height: 150,
    photonTube: '0'
   }
  }
 },

 constructor: function() {
  this.callParent(arguments);
  this.fire();
 },

 fire: function() {
  var surface = this.config.surface;
  var stageWidth = surface.element.getWidth(true);
  var stageHeight = surface.element.getHeight(true);

  var centerX = stageWidth / 2;
  var centerY = stageHeight / 2;

  // start from left or right of the draw component view?
  switch (this.config.photonTube) {
   case 0:
    this.setAttributes({
     x: -150,
     y: stageHeight
    });
    break;

    case 1:
     this.setAttributes({
      x: stageWidth,
      y: stageHeight
     });
     break;
  }
  
  // set animation time to complete (1.5 seconds)
  this.fx.setDuration(1500);

  // set animation transition
  this.fx.setEasing('easeOut');

  // set animation callback
  this.fx.on('animationend',this.onAnimationEnd, this);

  // apply changes to sprite, over 1.5 seconds

  this.setAttributes({
    x: centerX - 20,
    y: centerY - 20,
    rotationRads: 3,
    width: 40,
    height: 40
  });
 },

 onAnimationEnd: function(animation) {
  this.destroy();
 }
});

What are you waiting for? Go get your ‘pew-pew’ on!

And don’t forget to contact Fig Leaf Software for all of your Sencha-related consulting and training needs!

Building a Notification App for iOS with Sencha Touch and PhoneGap – Part 1 of 4

Push it….Push it real good!

At Fig Leaf Software, we’re seeing increased demand to add push notifications to our customer’s mobile apps. Push notifications are a great way to notify your customer base about important events.

While Sencha Touch 2.x directly supports push notifications through its Ext.device.Push class,  as of this writing, its support is limited to iOS. We therefore opted to use a PhoneGap-based solution, described below, that supports both iOS and Android in order to support a broader audience.

(click here if you didn’t get the salt & pepa reference above)

  • Part I of this tutorial focuses on using Apple’s developer portal to generate the required certificates necessary for app compilation and testing. It also describes how to prepare a PhoneGap project to support push notifications.
  • Part II of this tutorial focuses on using PHP on a test server for sending push notifications to Apple. (coming soon!)
  • Part III of this tutorial describes how to integrate the PhoneGap Push Notifications plugin with your Sencha Touch application. (coming soon!)
  • Part IV of this tutorial describes how to make your application available to beta testers

Prerequisites

This tutorial assumes that you have procured an iOS developer account ($99 USD) and that you’re working with Sencha Touch 2.2+ and Sencha Cmd 3. You’ll also need a physical iOS device (I used an iPod Touch) and tethering cable, since notifications are not supported by the iOS simulator. No prior experience with compiling a Sencha Touch app is necessary to complete this tutorial.

PART I: Download the required software

  1. Download and install XCode
  2. Download PhoneGap 2.50
    Note: Sencha Touch seems to have some difficulties with Phonegap > 2.50
  3. Download the Cordova PushNotification Plugin
  4. Download the Cordova openUDID plugin
    Note: This is used by the Cordova PushNotification plugin
  5. PHP for OS/X, which you can enable by following these instructions.

Part II: Generate a Development Cert for your App

  1. Log into the iOS provisioning portal
  2. Click the link for Certificates, Identifiers, and Profiles
  3. In the iOS Developer Portal, click on Certificates
  4. Click on the [+] button
  5. Select iOS App Development and click Continue
  6. Follow the on-screen instructions to create a CSR file and click Continue.
  7. Click Choose File and select the CSR file that you created in the previous step.
  8. Click the Generate button.
  9. Click the Download button
  10. Double-click on the .cer file to install it into Keychain Access
  11. Return to the iOS Developer portal and click the Add Another button.

PART III: Register your App with the iOS Provisioning Portal

  1. Log into the iOS provisioning portal
  2. Click the link for Certificates, Identifiers, and Profiles
  3. Click on Certificates
  4. Click on App Ids
  5. Click on the [+] to define a new App ID
  6. Fill out the form:
    1. Enter an App ID Description, e.g. ‘Fig Leaf Software Notifications’
    2. Under the App Services heading, check the box labeled “Push Notifications”
    3. Enter a bundle id  in reverse domain style, e.g. ‘com.figleaf.notifications’
  7. Click the Continue Button
  8. Click the Submit Button. Your App’s definition should resemble the following:
    Registering an App

Part IV: Create a Push Notification Development Certificate

This is used by your server to communicate with Apple’s Push service. Each app will have its own development and production push notification SSL certificate.

  1. Click back on App IDs and  select your app. Note that the items labeled Push Notifications are marked as “Configurable” as illustrated below:flsnotifications
  2. Click the Settings button. At the bottom of the screen, under the Push Notifications heading, you should see output similar to the following:a1
  3. Click on the Create Certificate button under the Development SSL Certificate.
  4. Go through the process to generate the certificate.
  5. Click on App IDs
  6. Click on your application
  7. Click on the Settings button
  8. Click on the Download button to download the Development SSL Certificate for Push Notifications
  9. Double-click on the certificate that you downloaded in the prior step in order to install it into KeyChain Access.

Part V: Registering Test Devices and Defining a Provisioning Profile

  1. Open XCode
  2. Tether your iOS device to your computer.
  3. Select Window > Organizer
  4. Click on the Devices tab.
  5. In the Devices column, click on your device. Organizer should appear similar to the following:organizer
  6. Copy the device’s identifier to your clipboard
  7. Return to the iOS Provisioning portal in your browser.
  8. Click on Devices.
  9. Click on the [+] to register your device.
  10. Register your device as illustrated below:
    AddIOSDevice
  11. Click Continue
  12. Click on the Provisioning Profiles link
  13. Click the [+] to add a new Provisioning Profile
  14. Click the Radio button labeled iOS App Development
  15. Click Continue
  16. Select your App from the select list and click Continue.
  17. Choose your iOS Development Certificate and click Continue.
  18. Choose your device and click Continue
  19. Enter a name for your profile, e.g. Notification App Testing
  20. Click the Generate button.
  21. Click the Download button.
  22. Double-click on the downloaded mobileprovision file to install it into KeyChain access.

Part VI: Create a Phonegap Project

  1. Unzip PhoneGap to a temporary directory
  2. Create a folder in your webroot named Cordova
  3. Copy PhoneGap’s lib/ios folder to webroot/Cordova. This should give you a /webroot/Cordova/ios folder.
  4. Open a command prompt to /webroot/Cordova/ios/bin
  5. Type the following command to generate an Xcode project:
    sudo .create [path to project folder] [application bundle id] [application name]
    

    e.g.:

    sudo .create ../../notifications com.figleaf.notifications FigLeafNotifications
    
  6. Using Finder, set read/write permissions on the generated project folder and all of its files.
  7. Verify that your project folder resembles the following:notificationprojfolder

copying

Part VII: Install the PhoneGap Notification Extension into your XCode Project

  1. Double-click on your .xcodeproj file to launch Xcode.
  2. Unzip the Cordova Push Notification Plugin that you downloaded in Part I/Step 3 into a temporary folder.
  3. Rename the Cordova Push Notification Plugin’s src/ios folder to ‘PushNotification’
  4. Drag and drop the ‘PushNotification’ folder from finder into your project’s ‘Plugins’ folder in XCode. When prompted, select ‘Create groups for any added folders’
  5. Copy the Cordova Push Notification Plugin’s www/PushNotification.js file into your Xcode project’s www folder.
  6. In XCode, open your project’s config.xml file.
  7. Add the following entry to the file’s plugins section:
    <plugin name="PushNotification" value="PushNotification" />
  8. In XCode, open classes/AppDelegate.h
  9. Append the following line just below the other imports:
    #import "PushNotification.h"
    
  10. In XCode, open classes/AppDelegate.m
  11. Append the following code just before the @end statement located at the end of the file:
     #pragma - PushNotification delegation
    
        - (void)application:(UIApplication*)app didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken
        {
            PushNotification* pushHandler = [self.viewController getCommandInstance:@"PushNotification"];
            [pushHandler didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
        }
    
        - (void)application:(UIApplication*)app didFailToRegisterForRemoteNotificationsWithError:(NSError*)error
        {
            PushNotification* pushHandler = [self.viewController getCommandInstance:@"PushNotification"];
            [pushHandler didFailToRegisterForRemoteNotificationsWithError:error];
        }
    
        - (void)application:(UIApplication*)application didReceiveRemoteNotification:(NSDictionary*)userInfo
        {
            PushNotification* pushHandler = [self.viewController getCommandInstance:@"PushNotification"];
            NSMutableDictionary* mutableUserInfo = [userInfo mutableCopy];
    
            // Get application state for iOS4.x+ devices, otherwise assume active
            UIApplicationState appState = UIApplicationStateActive;
            if ([application respondsToSelector:@selector(applicationState)]) {
                appState = application.applicationState;
            }
    
            [mutableUserInfo setValue:@"0" forKey:@"applicationLaunchNotification"];
            if (appState == UIApplicationStateActive) {
                [mutableUserInfo setValue:@"1" forKey:@"applicationStateActive"];
                [pushHandler didReceiveRemoteNotification:mutableUserInfo];
            } else {
                [mutableUserInfo setValue:@"0" forKey:@"applicationStateActive"];
                [mutableUserInfo setValue:[NSNumber numberWithDouble: [[NSDate date] timeIntervalSince1970]] forKey:@"timestamp"];
                [pushHandler.pendingNotifications addObject:mutableUserInfo];
            }
        }
    
    
  12. Add the following block inside the ‘- (BOOL) application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions’ function,  just before the return YES.
       // PushNotification - Handle launch from a push notification
        NSDictionary* userInfo = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
        if(userInfo) {
            PushNotification *pushHandler = [self.viewController getCommandInstance:@"PushNotification"];
            NSMutableDictionary* mutableUserInfo = [userInfo mutableCopy];
            [mutableUserInfo setValue:@"1" forKey:@"applicationLaunchNotification"];
            [mutableUserInfo setValue:@"0" forKey:@"applicationStateActive"];
            [pushHandler.pendingNotifications addObject:mutableUserInfo];
        }
    
  13. Unzip the Cordova openUDID plugin that you had previously downloaded into a temporary folder.
  14. Drag and drop the OpenUDID.h and OpenUDID.m files into your XCode project’s Classes folder.
  15. In XCode, open www/index.html and add the following <script> tag after the <script> tag that loads cordova-2.5.0.js:
    <script type="text/javascript" src="PushNotification.js"></script>
    
  16. Select your device from the list of test devices as illustrated below:xcode
  17. Click the Run button. The app should compile successfully and install to your device.

— End —

Localizing Sencha Touch Applications for the Cheese Eating Surrender Monkeys

Ever since I implemented Voice of America’s content managed web site (b. 1999, d. 2005) and had to support 44 different languages, I’ve had a rather strange interest in developing multilingual apps. I consider it to be ‘strange’ because no one could have predicted that the guy who once got a D+ in junior high French class would go on to accumulate a ton of experience in successful enterprise application localization. As you can tell from the title of this post, I’m not bitter about the grade that I received 30 years ago …not bitter…at…all. For as I told my teacher, “I already speak three languages: Basic, Pascal, and English. French seems rather superfluous.”

As an adult, I’ve repeatedly told my kids “You can learn any foreign language that you’d like, as long as it’s Java, C++, or Javascript. Just stay the hell away from COBOL – for it’s the modern day equivalent of Latin.” They stare at me blankly, quickly turning back to playing Minecraft.

But.. I digress.

Since my consulting practice is based in Washington, DC, I generally don’t get a lot of opportunity to use my i10N-fu…but when an opportunity comes around, I like to dig deep.

To support latin-based, left-to-right languages (English, Spanish, French, Italian, etc), there are four major concerns:

  1. All text prompts need to be translated into language
  2. Enough space needs to be allocated in order to accomodate variable string lengths in word spelling
  3. Text translations should be encapsulated into separate, easy to edit files so that translations can be easily outsourced to human beings (Google Translate just doesn’t cut it…yet)
  4. Dates and numbers need to be translated into the locale. The French, for example, swap their commas and periods which I’m fairly confident was a central cause of the European banking crisis.

Dynamically Assigning Text Prompts

As the code listing below illustrates, all on-screen prompts are implemented as properties of the View class. Prompts are assigned at runtime in the initialize() method of the view.

Ext.define('MyApp.view.Login', {
	extend: 'Ext.form.Panel',
	alias: 'widget.loginform',
	requires: [
		'Ext.field.Email',
		'Ext.field.Password',
		'Ext.Button'],

	txtEmailPrompt: 'Email:',
	txtPasswordPrompt: 'Password',
	btnSubmitPrompt: 'Submit',
	headerHtml: 'Heading Info',
	footerHtml: 'Footing Html',
	loginErrorMsg: 'Sorry, try again',

	config: {
		scrollable: null
	},

	initialize: function() {

		this.setItems(
		[{
			xtype: 'component',
			itemId: 'header',
			html: this.headerHtml
		}, {
			xtype: 'emailfield',
			name: 'email',
			label: this.txtEmailPrompt,
			labelWidth: '40%'
		}, {
			xtype: 'passwordfield',
			name: 'password',
			label: this.txtPasswordPrompt,
			labelWidth: '40%'
		}, {
			xtype: 'button',
			text: this.btnSubmitPrompt
		}, {
			xtype: 'component',
			itemId: 'footer',
			html: this.footerHtml
		}]);
		this.callParent(arguments);
	}
});

Localizing Text Prompts and Applying Translations to Views

The next step is to encapsulate the translations into a class that can be easily edited. In Ext JS 4, the standard is to place translations in separate files that are read in at runtime. However, since I’m dealing with a mobile app, I wanted to minimize the http overhead and decided to place all the translations into a singleton that is packaged along with the rest of the app.

Note the following:

  • Each language is represented by a two-character language code
  • Translations are grouped by the view in which they’ve been defined
  • The localize() method applies the translations to each of the view classes for which a translation is available.
Ext.define('MyApp.config.Locale', {
 singleton: true,
 config: {
  en: {
   Login: {
    txtEmailPrompt: 'Email:',
    txtPasswordPrompt: 'Password',
    btnSubmitPrompt: 'Submit',
    headerHtml: 'Heading in English',
    footerHtml: 'Footer in English',
    loginErrorMsg: 'Sorry, try again'
   }
  },
  fr: {
   Login: {
    txtEmailPrompt: 'Votre email:',
    txtPasswordPrompt: 'Votre mot de passe',
    btnSubmitPrompt: 'Transmettre',
    headerHtml: 'Heading en Francais',
    footerHtml: 'Footer en Francais',
    loginErrorMsg: 'Merde. Où sont les toilettes?'
   }
  }
},
localize: function(locale) {
 var translations = this.config[locale];
 for (var view in translations) {
   Ext.apply(MyApp.view[view].prototype,translations[view]);
 }
}
});

Using this technique, I’m able to easily apply the translations just prior to launching the main view of the app:

Ext.application({
 name: 'MyApp',
 requires: [
  'Ext.MessageBox',
  'MyApp.config.Locale'
 ],
 views: ['Main'],
 launch: function() {
      MyApp.config.Locale.localize('fr');
      Ext.Viewport.add(Ext.create('MyApp.view.Main'));
 }
});

dateselectorLocalizing Dates

Unfortunately, the aforementioned solution is still a “half-measure” in that we still need to localize the built-in Sencha Touch controls, such as the date selector. In order to do this, you’ll need to override the Ext.Date class (which loads the Ext.DateExtras class as a mixin). Fortunately, the Ext.Date/Ext.DateExtras classes in Sencha Touch closely parallel Ext JS 4, so you can repurpose Ext JS 4 locale files as illustrated below:

Ext.define('MyApp.config.Locale', {
 singleton: true,
 config: {
  en: {
   LoginView: {
    txtEmailPrompt: 'Email:',
    txtPasswordPrompt: 'Password',
    btnSubmitPrompt: 'Submit',
    headerHtml: 'Heading in English',
    footerHtml: 'Footer in English',
    loginErrorMsg: 'Sorry, try again'
   }
  },
  fr: {
   LoginView: {
    txtEmailPrompt: 'Le Email:',
    txtPasswordPrompt: 'Password',
    btnSubmitPrompt: 'Submit',
    headerHtml: 'Heading En Francais',
    footerHtml: 'Footer En Francais',
    loginErrorMsg: 'Sorry, try again'
   }
  }
 },

 fr: function() {
    // date localization borrowed from Ext JS 4 fr locale

   if (Ext.Date) {
    Ext.Date.shortMonthNames = ["Janv", "Févr", "Mars", "Avr", "Mai", "Juin", "Juil", "Août", "Sept", "Oct", "Nov", "Déc"];

    Ext.Date.getShortMonthName = function(month) {
	return Ext.Date.shortMonthNames[month];
    };

    Ext.Date.monthNames = ["Janvier", "Février", "Mars", "Avril", "Mai", "Juin", "Juillet", "Août", "Septembre", "Octobre", "Novembre", "Décembre"];

    Ext.Date.monthNumbers = {
     "Janvier": 0,
     "Janv": 0,
     "Février": 1,
     "Févr": 1,
     "Mars": 2,
     "Mars": 2,
     "Avril": 3,
     "Avr": 3,
     "Mai": 4,
     "Juin": 5,
     "Juillet": 6,
     "Juil": 6,
     "Août": 7,
     "Septembre": 8, 
     "Sept": 8,
     "Octobre": 9,
     "Oct": 9,
     "Novembre": 10,
     "Nov": 10,
     "Décembre": 11,
     "Déc": 11
    };

    Ext.Date.getMonthNumber = function(name) {
	return Ext.Date.monthNumbers[Ext.util.Format.capitalize(name)];
    };

   Ext.Date.dayNames = ["Dimanche", "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi"];

   Ext.Date.getShortDayName = function(day) {
    return Ext.Date.dayNames[day].substring(0, 3);
   };

   Ext.Date.parseCodes.S.s = "(?:er)";

   Ext.override(Date, {
    getSuffix: function() {
     return (this.getDate() == 1) ? "er" : "";
    }
   });
 }
},

en: function() {
  // reset all dates to english here
},


localize: function(locale) {
  var translations = this.config[locale];
  for (var view in translations) {
    Ext.apply(MyApp.view[view].prototype, translations[view]);
  }
  this[locale]();
 }
});

So, as my old french teachers would have said…”C’est Bon!”..right after giving my latest effort a C-.

Finishing up Fast Track to Sencha Touch 2.2 Course Updates

I‘m wrapping up delivered  on my latest courseware collaboration with the Sencha training team to update their Fast Track to Sencha Touch 2.2 coursebook. They’ve really done an awesome job with this release. I couldn’t find a single major bug and the performance improvements are quite phenomenal. They also now support the new Windows phones, which is pretty cool.

As with previous releases of the courseware, you’ll learn how to develop a complex, custom-themed production-ready app named “CrimeFinder” that brings information about criminal activity in the DC area to your mobile device. As indicated by the screenshots below, you’ll use a single codebase to easily support multiple device profiles. With over 45 exercises spread over five days, you cannot find more comprehensive instruction on how to develop cross-platform mobile apps!

Image

Sencha has a really skilled network of instructors (including myself) ready to teach a private class to your development team. Contact Fig Leaf or Sencha for more information.

You can also sign up for open-enrollment training courses (offered worldwide) at http://www.sencha.com/training/.

I’ll be teaching the next open enrollment class in Washington, DC on June 17. The course is starting to fill, so sign up today! I love showing off the latest and greatest stuff that I’ve been working on for The Fig’s consulting services group as well as counsel students on how to apply concepts taught in class apply to their work-related projects. SO BRING YOUR CODE TO CLASS!

What are you waiting for? Sign up now and join the mobile revolution!