Transcript
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
>> Alright, good
morning everyone.
I'm Doug Davidson, and
I'm here to talk to you
about making your
app world ready.
So, the app store
is available in more
than 150 countries worldwide,
and if your application is
only targeted at one of them;
not only are you missing
out, but also a lot
of your potential users
are being deprived as well.
So, we're going to tell you how
with a little additional effort,
you can make your app ready
for international use.
The steps to go through
are fairly simple.
In many cases, we find
that what you have to watch
out for is assumptions
that you may be making;
maybe even unconsciously,
that just don't hold
everywhere in the world.
And, so we'll be
talking a lot about that.
Often, it's not things you don't
know that will hurt so much,
as the things you do
know that just aren't so.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Now, there are some
genuine challenges here.
Our customers around the world
use many different languages,
many different writing systems,
and they have often
significantly different
expectations after the
presentation of items like dates
and times and numbers.
But, fortunately, the frameworks
are designed to handle this,
if you work with them.
Now, I say frameworks,
it's important to note
that almost everything
we're going talk
about here today applies
to both iOS and OS X.
Now, we're going to do
this in three parts.
The first is about localization.
Localization refers
to the language
in which you application's
user interface is presented.
Your job as a developer
is to make sure
that your application is
localizable, so that you can go
to the localizer, who
will translate the text
in your user interface into
any particular language,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
then all you have to do is
decide how many languages you
want to do this for.
Second, we'll talk about
locale data, and that refers
to presenting things like
dates and times and numbers
in a way that's intelligible
to people
in that particular region.
And, finally, we'll talk
about handling texts
and all the different writing
systems used around the world.
There are two main settings
that are involved here,
and this is what they
look like on iOS.
The first is the user's
language preference,
and that's what determines
which localization
your app will run in,
and the second is the
user's region preference,
which determines what their
locale settings will be.
On OS X Mavericks, we have
an entirely new pref pane
for handling this.
It's designed to streamline it,
and make changing
the settings simple,
and make it all more logical.
It's very similar.
You have the user's
language preference again
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
that determines what
localizations will be used,
and the user region preference
that determines the
locale settings.
Additionally, on OS X,
there is an advanced sheet
that allows the user to
customize in great detail,
if they want to, any of the
various settings that are part
of their locale preferences.
So, to start off with, we'll
talk about localization.
I'll bring up my colleague,
Albert Lund to talk about that.
[ Applause ]
>> Hello, I'm going to be
talking about localization,
and what localization is,
is that it translates
your application
from its current language
to another language,
so your app can provide much
more exposure and visibility
to other markets
around the world,
leading to more downloads
for your app.
It's also about adapting your
application to cultural norms
in those regions, so you
can provide the same level
of user experience for
all users of your app.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Now, today, I'm going
to be talking about how
to use our tools and X codes
to localize your application,
as well as some common
mistakes and issues
that may occur when localizing.
So, here's your project is
structured for localization.
You have your single binary,
which is your header files,
your implementation files,
and libraries you call,
and you only have one
set of these files.
You also have localization
folders alongside your binary,
and these are folders
called L proj folders,
or your language
specific project folders.
Each localization is placed
into one of these folders,
and it's abbreviated
by the language.
So, for example, the English
location is EN, French is FR,
Spanish is ES and so on.
All of these localizations
are then placed
into your single app bundle,
and one of these will
get loaded at runtime.
Whichever one gets loaded
at run time is determined
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
by what the user selected
system language is.
So, if the user's
language is Spanish,
the Spanish localization
gets loaded.
So, here's how your
project is structured.
You can see that the L proj
folders are alongside your
implication files
and your localizers
or your translators just have
to modify these folders only;
they don't need to
touch anything else.
These folders contain things
such as your strings files,
which contains things such
as your user visible text,
as well as any resource
files, such as images
and anything else you might
want to use for localization.
So, let's get started on
how to localize things
such as your interface files or
your nib files or storyboards.
The old way to do this, and
you can still do this today,
is to make a copy of
every single nib file
for every single
localization you have.
Your localizers then just open
and modify the specific
interface file
for that specific
localization, modify the text
so it's localized, and make sure
that everything surrounding
it looks good
and works for this interface.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Like I said, while
these work today,
if you want to localize
this way,
you can see this
gets very cumbersome,
and can be very time consuming
for you and your localizers,
so we highly recommend
you use this method called
base internationalization.
With base internationalization,
you only need to modify one set
of storyboards and nib files,
so you don't have to worry
about localization when it
comes to your interface.
Every single time you
create a new localization,
a strings file, which
basically takes all
of the user visible text out of
your nib file, is then placed
into those specific
language folders,
and your localizer just has to
modify those, the text only.
They don't need to touch
your interface at all.
Now, it's highly recommended
that you should use auto layout
for base internationalization,
and I'll explain what auto
layout is in a moment.
But, here's an example how base
internationalization works.
I have my one nib file here,
and this is my base
internationalization file.
As a developer, I only need
to modify this one file,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and every single time I
create a new localization,
a strings file is then
generated for every single one.
From there on, my localizers
then do not touch anything
involving my interface.
They only need to
modify the strings,
and that's really convenient.
So, what's auto layout.
Auto layout is a way to define
the constraints between text
and anything surrounding your
text, such as text views,
UIImageViews, and
pretty much anything
that surrounds the text.
It is a way to appropriately
resize everything depending
on the length of the text, which
is crucial for localization;
the reason being is that when
you translate your application
from one language to another,
some translated strings may be
significantly longer or shorter,
and you want to be able to
dynamically adjust these,
so your interface looks good.
And, to learn more
about auto layout,
I highly recommend you check out
the video for "Taking Control
of Auto Layout in X Code 5".
That was held yesterday.
So, let's get started on how to
use base internationalization.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So in my project
window for X code,
I have this checkbox here called
used space internationalization.
I check this box, and then
X code will then ask me
which files do I want to use for
my base internationalization.
It will then move all
of these interface files
and any file I want into
the base.Lproj folder,
and anytime I want to
create a new localization,
I then push this
plus button here.
Any interface files
then that are placed
in the base.Lproj folder
will then generate their own
strings files.
And, then again, my
localizer just has to go
into these strings file
only, and have nothing to do
with the interface,
which is great.
Now, when you make an update to
your interface, you also want
to make sure that your interface
is also localized as well.
And, we have a command line
tool in OS X; we call it IB12,
which will generate a
strings file for you.
So, in this example here
in my base.Lproj folder,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
I just pass in the nib file that
I have changed, and run IB12
on it, and it will generate a
new strings file containing all
of the strings that
were originally
in this interface file.
I then go in and open
this new strings file,
copy and paste everything
that has changed,
or anything that I have
updated or added, and paste it
into every single strings file
that has this interface
file here,
so it's very good to update.
Now, some issues that can come
up with using auto layout,
is that you may use the
fixed width for auto layout.
This entirely defeats the
purpose of auto layout,
since you want to make sure
that all text is
resized automatically,
and you don't want your
interface to look bad
on certain localization
because you decided
to have a fixed width
between text
and UITextView or something.
And, as always, it's good
to prefer an intrinsic content
size instead and as always
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
if you, you should always
try out your layouts
in every single localization
and test,
so your app in those
localizations.
Because what's works
in one language,
and what constraints may
work in one language,
may not necessarily
work in another.
So, let's talk about
the strings file.
The strings file contains
the user visible text
that will get localized
and displayed in your app.
The strings file is placed
into a key value table,
and what happens is,
is that the left side
of the table is your key.
The key is what the application
is going to be looking
for to display what localized
text should be displayed
to the user.
So, in this example here, I have
three localized strings files
that are placed in three
separate localizations;
the English, simplified
Chinese, and Spanish.
At run time, depending on what
localization my user is in,
one of these will get loaded,
and one of these
will get displayed.
So, if my application is looking
for a name key, it will look
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
in the name field for that
specific localization,
and display whatever is
in the user visible text.
Now, you will also have
some strings in your code
or some user visible
text in your code
that you want localized as well.
To do this, you should
use NSLocalizedString
and use NSLocalizedString
everywhere
where you have user
visible text,
and NSLocalizedString
has many variants
that pretty much can do
what you want it to do,
so check out more details
for NSLocalizedString
in the release notes.
Here's an example of
NSLocalizedString used
in its most common case.
It takes in two parameters;
one is a key,
which what will get mapped
to the localized text
that you want, as well as
a comment to the localizer,
which I'll talk about later.
Once you localize your
strings in your code,
you want to make sure that
you create a strings file
that will have all these
NSLocalizedStrings in one place.
You can do this manually,
but there is a script
that will do this for you, and
it's highly recommended you use.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
It's called gen strings,
and it's highly scriptable
and customizable.
You can add it to all of
your X code billing phases,
and to learn about gen strings,
you just check out
the amend page.
So, here's an example of
how gen strings is invoked.
What happens here is that I
look in the local directory
for all files that end in
.m, and run gen streams
of every single one;
meaning that it will go
into all .m files, search
for NSLocalizedString,
and place a localizable
.strings file
into the English localization
folder in this case.
So, I have a case here
when I've run gen streams
on two separate localization,
my .m file contained this
one NSLocalizedString,
and I placed the strings file
in the English localization
and the Japanese localization.
You can see that the comments
and the key have been placed
in the appropriate
positions, and now my localizer
or my translators just have
to modify the right side
of the table to localize
it to whatever language
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
that this is in, and
this is done right here.
Some issues that can come up
with using NSLocalizedString is
that you overload your keys,
meaning you have one key
that maps to many
different places.
Let's say in your application,
you ask the user if they want
to save, and you
have two buttons;
one for yes and one for no.
You also have another button
asking if they want to subscribe
to the super popular
catfacts, with a button
for yes and a button for no.
You have one NSLocalizedString
that maps to both of these,
and this seems to work perfectly
fine for your application.
However, later on in
your development process,
you start to realize that
yes and no aren't very clear,
and should be possibly save
and cancel, but in doing so,
you replace the string and you
inherently break the other part
of your application, and save
and cancel doesn't
really make any sense.
So, it's very important
that you make sure
that all user visible text
has its own unique key,
so you don't run into
this issue where one part
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
of your application
inherently breaks another.
Also notice here that
when I made the change
to NSLocalizedString,
the comments were also
updated appropriately
to reflect what was going on,
which leads me to
the second issue.
You provide no comments
to the localizer
or insufficient comments,
it is your localizer's worst
nightmare, and possibly
like will hate you for this,
if you add no comment provided,
and yes equals yes,
or not enough context.
While in English, for example,
yes can mean many different
things, in some languages,
such as Chinese, the word yes
has many different possible
translations, and
without any context
or knowing exactly what's
going on in your application,
your localizer will
pretty much be able
to translate this
part of your app.
So, it's very important that
you provide enough context
to the translators, so
they know exactly how
to translate your
application properly.
The third issue is
composing phrases together,
which is something we as
engineers do quite often;
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
the reason being is that if
you compose phases together,
some languages have
these conjugation rules
and some grammatical rules
that depend on other parts
of a phrase or sentence,
and without knowing the
previous parts of your sentence,
it will be grammatically
incorrect.
So, for example here, I
have three strings here,
one for go to next blank,
chapter, and then page.
In English, this seems
to work perfectly fine,
since go to next chapter and
go to next page are valid
and grammatically correct.
However, if I'm a
Spanish localizer,
and I try to translate this, I
see go to next and then chapter.
Since chapter is
a masculine noun,
and I must have a masculine
article in order to agree
with chapter, this is going
to work perfectly fine.
When it comes to page, since
page is a feminine noun,
the article must be feminine,
but if I've already
made it masculine,
then this is inherently broken,
and this localization
is broken for Spanish.
So, it's very important
to make sure again
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
that your user visible text
each has their own unique key,
and you compose things in
a sentence wise, or else,
you're going to run
into grammatical issues.
There are going to be
some cases, however,
when you are going to have to
dynamically create strings,
such as when it comes to
the case of pluralization.
This is cases where if I want
to display to the user I have 0
of something remaining, one
of something, or more than one
of something, and
in some languages,
this can get very complicated,
where there's a different string
for two to five of something or
10-14 of something and so on.
There is a new feature
in OS X Mavericks
and iOS 7 called stringsdict,
which a localized plist
that essentially
handles all these cases
of pluralization for you.
You as a developer do not
need to call any new API's,
since stringsdict will
be called automatically,
if it's available, and your
localizers then just have
to fill out this plist,
and will handle all
these cases for you.
To learn more about this in
complete technical detail,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
check out the foundation
release notes, but if you want
to learn how to get the setup
and have a brief tutorial
about this, check out the
"What's New in Cocoa Video"
for the session that
was held two days ago.
So, here's an example
of stringsdict
on two separate localizations;
one for English and
one for Russian.
You can see that the English
localization has very simple
case with one and
anything but one,
and the Russian case can
get very, very complicated.
Now, aside from text, which
is the most localizing you'll
probably be doing, you can
localize other things as well,
such as your images,
your sound files,
and pretty much anything
can be localized.
To do this, you create a
localized version of this file,
and place it into the
respective localization folder
that you wish to load for
this specific localization.
The API's you called to call
for these specific files
will automatically call the
localized version.
So, there is no need
for any extra code
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
to handle for localization.
So, let's say I have an image
here that I want to localize.
I push this localize
button here, right there,
and X code will then
ask me where do I want
to place this localization file.
In this case, I want
to move this image file
to the English localization,
so this file will get loaded
for the English localization.
Any single time I want to
create a new localized file
for this specific image here,
I just have to create a file
with the same name,
and just place it
into the specific
localized folder that I want,
so for example here, I have an
RTF file that has been localized
to several different languages,
and all of these
RTF files are placed
into their respective
L.proj folders.
Now some issues that
can come up is
that you have some
text in your image.
While it's okay to have
text in your image,
be aware that your localizers
will have trouble translating
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
text in your image, as opposed
to just translating text itself,
since it requires
a bit more effort
to translate text in your image.
The other issue is having
an image or something
to convey a meaning to the user,
since some images may
not necessarily work
in other languages.
So, for example I have a
keyword search, and I use a key
to denote a keyword search.
In English, this seems
to work perfectly fine,
since I see a key, I can think
of keyword, since, you know,
there's the same word,
but in other languages,
the word keyword doesn't
necessarily translate to a key
at all, since you'd come out
to head word or focus word
or indicative word, and your
users will pretty much have no
idea why you have
a key for keyword,
so the idea is completely
lost in translation.
Another issue is having
a cultural reference,
or something that's specific
to a specific culture,
and this may run
into some issues.
So, for example, in Japan,
this image here is placed
onto the back of all cars for
anyone who is inexperienced
or a beginner driver
to alert other drivers
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
that she should probably
get out of the way.
Some Japanese developers
sometimes use this to show
that this is a tutorial or a
beginner's guide to something,
and if someone isn't well
versed in the Japanese culture,
or doesn't know anything
about Japan,
they pretty much have no
idea what this icon is,
and they'll get lost
in your application.
So, make sure that any
images that you have
that display a meaning or try
to convey a meaning to the user,
is workable for all the
localizations you want
to support.
Now once you've localized
your application,
you'll want to test this out,
and the most accurate way
to do this, and to see exactly
what your user is going to see,
is to change the system
language inside of your system
to the language you want.
Also, the other reason is,
is that some system services may
not work in that localization
until you explicitly send the
language to that localization.
However, if you want to
quickly check whether
or not a string has
been localized
or a file loads correctly,
you can run your application
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
in X code using the dash
Apple language as argument.
And, in this example here,
no matter what language my
system is in, it's going to run
in the Korean localization.
A really cool way to
check whether or not
if your application
has been localized,
and to test out whether
your interface will work
for longer strings,
longer strings,
is to use pseudo localization.
So, what's pseudo localization?
Pseudo localization
is a way, well,
if you have your strings
files, you can run a script
through the user visible text
in your string to modify it
and distort it, such
that it is longer
than what's originally there,
and will distort it to the point
where if you were looking
in your application
to check whether or not some
strings have been localized,
you can obviously tell whether
you're missing some parts
or you forget to
localize something.
And, with that, I'd
like to bring back Doug
up to show us a demo on
how localization works.
[ Applause ]
>> Thanks Albert so in
order to demo this out,
we wrote a tiny little
application.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So, what this application
does is it lets me type
in some brief little notes,
and then it shows them
in a scrolling list,
really simple.
And, so the first version
of this application
that we have here is
not localized at all.
Notice, it's not using
base internationalization;
that's turned off.
The only localization that's
there is English, and if I look
at my nib file, it's all in
English, and if I take a look
at my code, I notice that there
are some user visible strings
in here; for example when
I'm creating an alert,
they're just static
explicit strings,
and for setting the title of
the window to reflect the number
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
of notes, I have
switched between one note
or multiple notes, so
it's all in English.
So, what that means is that if
I go in and change my system
into say German, and then run
the app again, nothing changes
at all, it's not localized.
So, the next thing to do
is to create a new version
of this application,
and we turned
on base internationalization.
What that does is create
a base localization,
and that's where
our nib file goes,
and then we added an additional
German localization to this app.
So, if we take a look at our nib
file, it has not changed at all.
The only thing that happened
to it is that it moved
from the English localization
into the base, and in addition,
we get a strings file
associated with it
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
that contains the
translations; we added a rough
and ready German translation
here, to the various strings
that appear in that nib
file, and then we run the app
in German, these will
automatically be substituted
into that nib, in
place of all the places
where the English text appears.
In addition in code, we
took these explicit strings,
and replaced them calls
to NSLocalizedString
with suitable keys and suitable
comments for the localizer,
and then for the title that
reflects the number of notes,
we're going to use
a string sticks,
to get appropriate
localization of plurals
in whatever language we're
translating this into.
So, we ran gen strings on
this, and the result is
that we have localizable
string files for English,
and then for German, where we
put in appropriate translations
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
for each of the keys that
appear in our NSLocalizedString,
and created string
sticks for details
on the format string
sticked file.
You can again, take a look at
the foundation of these styles.
If explains it all in detail,
but the business part of it,
has these lines that
describe what to do for,
when the number is
one, and what to do
when the number is
other than one.
So, for English, we chose
a very simple set of rules.
For German, it got a
little more complicated.
We have rules for 0,
one or anything else.
And, let's see what
happens when this ends
up in our built application.
So, here we're taking a
look inside the app bundle.
In the resources we
have a base L.proj
that contains our nib file, so
there's only one copy of that,
and then in German L.proj, we
have just the strings file,
the stringsdict, and that
credits file that X code wants
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
to create for us,
and when we run this,
then the main menu.strings
contents should be automatically
substituted into our nib.
So, let's try that out.
And, we see one right now, the
whole interface is in German.
We got our strings sticks
has given us a proper title
for 0 items, and even
our menus are localized.
Even the Apple menu stuff, this
comes from outside our app,
all localized properly, and
our localized strings show
up in the alert.
So, that is the basic
process of localizing an app.
Let me go back to the slides.
And next, I want to bring
up my colleague, Nat,
to talk about dealing
with locale data.
[ Applause ]
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
>> Okay, hello, hello.
Thank you, Doug.
So, my name is Nat Hillard,
and for this section,
we will be going
over locale data.
So, another critical aspect of
making your app world ready,
is presenting numbers, dates,
times, and symbols in a manner
that is appropriate
for your users.
Now, to do this isn't
just a stylistic issue.
It can have practical usability
complications and implications
as well, and ultimately, you
can conceive of this process
as translating between
machine readable data
and user readable strengths.
Now, interestingly, this is
not a one to one mapping.
Things can get pretty
complicated.
Now, luckily, we provided a set
of API's for you that do a lot
of the heavy lifting for you.
So, in this section, I'll
be going over the first five
in this table, and Doug
will cover NSString
in more detail in the next.
So, first of all,
what is a locale?
In an abstract sense, it's a
collection of user preferences
with regard to formatting.
The users will set
this, as Doug mentioned,
in the region format
preference on iOS or on OS X
in the languages and
format preference pane.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
In a concrete sense, it
is represented in Cocoa
with the NSLocale API.
Usually as a programmer,
you will not be dealing
with the NSLocale
object directly.
Instead, interacting with it at
the class level or passing it
in as argument to other API's.
So, important to keep in mind,
however, is the distinction
between a local and
a localization.
Locale represents the formatting
standards for a particular user.
These are informed
by their region,
their language, and
their script.
On the other hand, localization
refers to the language
of the user interface.
Now, these can often
be the same,
but for a given user,
they may differ.
As an example of using locale
information to present,
or locale data to present
information to your users,
let's take a look
at formatting dates.
So, for this, we've provided
the NSDateFormatter.
This converts between NS
date machine readable data
and a string representation
for a particular user.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Often, you'll be working with
this and explicitly attaching it
to a text field within
your nib file.
On the other hand, if you
need to use it in code,
you can call localized
string from date;
passing in your NSDate, as well
as a date style and
a time style.
So, what do these date
and time styles look like?
Well, they differ in the amount
of information they're
presenting to the user
about your NSDate object.
Here, they can vary
from anything as short
as a short style, where you're
simply presenting numbers,
to the full style where
you spell everything out.
As well, as if you need
to suppress either one
of these, you can use no style.
So, to see what this looks
like, we call localized string
from date on the NS
date formatter class.
We pass in our date, and we
pass in medium style for date
and short style for time.
Now, keep in mind this will
do the heavy lifting for us,
and present this information
in a locale appropriate
manner for our given user.
So, here we have three locales,
English as used in the US,
French as used in France,
and Chinese as used in China.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Here we have the out of
the box representation
of a single NSDate object.
Here we have June 6, 2013,
a presentation familiar
in US English.
On the other hand in France,
this API has done the work
of presenting first the day
number, followed by the month,
followed by the year,
and without a coma.
Also, in Chinese, we presented
the year number first,
followed by the character
for year,
followed by the month
number, character for month,
and day number, followed
by character for day.
Likewise with times, we
present AM in the United States.
We don't provide either
AM or PM for France,
which traditionally uses
24 hour time, and in China,
we present the characters
for morning,
followed by our single
time object.
So, this is the preferred
way to work with NSDate.
Out of the box, calling a
class method, localized string
from date, you've gotten three
very different representations
from the same object.
If on the other hand, you
want to present a custom view
of this same NSDate object,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
you have to go beyond the
certain present packaged styles.
Keep in mind that the medium
style will always present the
month, the day, and the year.
Let's say for instance
though that you wanted
to only present the
month and the day.
So, when the default styles
don't meet your needs,
you create an instance of
the NSDateFormatter class.
From there, you create
a format string.
This is done with date format
from template, a class method
on interstate formatter.
To this, you pass
a format string.
Now, for more information
on this,
you can check Unicode
standard 35, which has a lot
of information on this.
Suffice it to say,
we've said here
that we want the day number,
followed by an abbreviated
form of the month.
This can go into
a lot of detail.
These format strings can present
everything from given variance
of abbreviations, as
well as fully spelled
out or number variance.
Also critically, we
pass here the locale,
NSLocale, current locale.
So, this is the correct to do
it, but, there is, oh, sorry,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
finally, we set the date format
on our date formatter instance.
Now, this is the
correct way to do it.
However, there is an incorrect
and naive way to do it.
Let's say you've read
Unicode standard 35,
and you've explicitly set
date format to a string
that you've read about here.
You want the month, followed by
the day, followed by the year.
Notice though that we haven't
got through the template object.
The template does
the work for us
of rearranging the components,
so they're appropriate
for a given locale.
Consequently though, when we
call from string from date,
we get a representation that
is the same for all three
of these very different locales.
So, this is not the
correct way to do this.
Instead, going through the
intermediary of date format
from template, it will do
the rearranging for us.
Consequently, if we pass in
lower case d, capital MMM,
day and number, basically
indicating that we wish
to convey day number and
month, and then call a string
from date, we get the
appropriate representation
for a given locale.
We have the month name
followed by number in the US,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
the vice versa in France, and
in China, we have month number,
followed by month
character, day, day character.
Likewise though, with NS dates,
let's take a look at
number formatting.
This is another area in
which there's a great degree
of variation around the world.
To help you with this, we
have NSNumberFormatter,
which is meant to mirror
an estate formatter.
As an example of the type of
variation you may come across,
you'll have everything
from the separator
between the thousands digit
and the decimal digit.
Likewise, even the
digits themselves,
something we may take
for granted, can vary.
On the right, we have an
aerobic Egyptian representation
of this same digit number.
Currency can differ
in the symbol,
as well as the separators.
Percentage size itself
can differ, as well again,
we see the digits
differing, and even numbers
that are not numbers,
so not a number can come
up differently in
different locales.
Again though one thing to avoid
is providing explicit format
strings, so those of you coming
from C world will be familiar
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
with the print F style
C format strings.
Here we call string from
format on NSString,
saying that we want a floating
point number with three digits.
Consequently though, we get
the same number for all three
of these very different locales.
This is not the correct
way to do it.
So, one way to do a very
simple in place fix,
is to instead called
localized string
of format on your NSString.
This is only for a code
that exists already,
and is a simple in place fix.
However, if you're making code
that will be used going forward,
we prefer using the
NSNumberFormatter.
Here you call localized
string with number,
pass in your NSNumber,
and pass in again,
an explicit format string.
And, this will do the
heavy lifting for you
in presenting a number
in a locale appropriate
manner for a given user.
So, like NSDateFormatter, we
have explicit preset styles.
Here's what these look like.
Let's start with an NSNumber
literal, denoted by the at sign,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
followed by the number, 1234.56,
decimal style will
do the correct thing.
Here on this table we have,
the left column is US English,
and on the right are
variants you may encounter
around the world.
Again, see in Italy,
we have the period
and the coma have flipped.
Currency style is
different in China.
Percentage style differs
in these two aspects again,
and even scientific style can
differ in that it uses the comma
to separate out the
significant digits.
Finally, the spell out style
itself can be dramatically
different between both locales.
So, let's go into more detail
about the NSLocale
object itself.
Traditionally, the
standard API's will do,
take into account this
information for you.
To give it as an information,
sorry, to give it as an argument
to an NSNumberFormatter
and NSDateFormatter,
you can call current
locale the class method
or auto updating current
locale, which will listen
for notification that indicates
that the user has changed your
locale as your app is running.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
From there, if you
want even more detail,
you can create an instance
of the NSLocale class.
From there, you can call object
for key with various keys
to get more information.
For instance, let's
say you want to see
if a given user uses
the metric system.
This information is stored in an
instance of the NSLocale class.
You call object for key
with uses metric system.
Likewise, you can get
the currency symbol,
or for a slightly
more detailed example,
you can get the beginning
and end quotation marks
for a given locale.
All this is available from an
instance of NSLocale class.
So, here, let's see what this
looks like in a real example,
where you call a
string from format,
and pass in our previously
obtained beginning quote string
and end quote.
As a result, we have the
appropriate quotation marks
for China, France, and Japan.
We have the angle
brackets for France
and the square brackets
for Japan.
Keep in mind though,
the distinction
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
between a locale
and a localization.
To get a given user's
localization, that is to say,
the user interface language
that the user is
running your app within,
you call preferred
localizations on an instance
of the NSBundle class.
On the other hand, to get the
locale you call current locale
on the locale object.
This is what you'll be passing
into the formatting objects
in order to obtain the
appropriate representation.
So, one final area in which
you'll see a lot of variation
around the world is in the
presentation of calendars.
We may take for granted
that this is the year 2013,
or year 2011, and you
can see that in fact,
this is simultaneously also
the year 1432, 2554, 5771.
Likewise, this era
is AD or CE here,
but in Japan, it's
the Hassay era.
The number of months per
year, length of the months,
the day of the week
itself can vary,
and even the transition
of years.
Now, here we have a transition
between the Showa period
and the Hassay period in Japan,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
which happened as
recently as 1989.
So, to interact with calendars,
we use the NSCalendar object.
This allows us to
do calculations
in a more intelligent way.
It also, it allows us to
obtain the information we saw
in the previous table, the
number of days in the month,
weeks in the year, etc. It also
allows us to get components
from the date, that
then we can add
on to additional NSDate objects
to get dates in the future.
Likewise, as I said, you
can do delta computations
between two NSDate objects.
So, keep in mind though that NS
date itself is an abstract point
in time.
You must interpret this NS
date object through the lens
of an NSCalendar if you're
presenting it to the user.
If you're using a
code internally,
of course it's still fine
to use the NSDate object.
So, let's see an example of
getting the components of a date
that may be of interest to you.
We here, call components
from date,
passing in our NSDate object,
and passing in the individual
units that we wish to obtain.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Keep in mind here, you'll
only obtain these units
that you explicitly specify
that are returned in the form
of an NSDateComponents object.
From there, you can
call day, month, year,
and era to get this
information out of the object
that you've just created.
You can then this NS
date components object
onto an additional NSCalendar
method to add these components
to an existing NSDate, and
obtain a date in the future.
Now, this is important because
calendar computations can
be complicated.
The common thing to
keep in mind is that,
let's say we do some simple
arithmetic and we say,
well a day is 60
x 60 x 24 seconds.
So, if I go 86,400
seconds into the future,
I will be at this time tomorrow.
However, keep in mind
that on the border
between daylight savings time
and non-daylight savings
time, this is not the case.
Likewise, one month is not
always 30 days in the future.
One year is not always
525,600 minutes,
despite what the song may say.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So, for more information
on this,
you can see tomorrow's
presentation,
date and time challenges.
I assure you its quite
complicated, but they'll go
into a lot of information
about this.
So, with that, I'll pack to
Doug, and we'll do a demo
of seeing this in real life.
[ Applause ]
>> Thanks, Nat.
Alright. So, let me go back
to my first crude
unlocalized application,
and notice that it prints a
little header in front of each
of my notes that shows an
index number; I've started
with a thousand, just so I
can illustrate some things,
and a date.
Unfortunately, the format I've
chosen for this is pretty crude.
Let's see what that
looks like in code.
So, I'm just creating
using string with format
from the NSNumber and NSDate
attached to my note object.
Now, that's the sort of thing
that I might want to use
for an internal machinery
for full representation,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
but it's not the sort of thing
I want to present to my users.
I really want to give them
locale appropriate presentations
of the number and the date.
So, let me go and see what
that looks like in my updated,
properly localized application.
So, what I've chosen
to do here is
to create an NSDateFormatter
and an NSNumberFormatter
to format these two objects.
Once I have those, it's
just a simple matter
of replacing the index
number and date in my string
with format with the
properly formatted results
of calling string
from number and string
from date using my
number formatter
and date formatter objects.
So, to do that, I have to
create the date formatter
and the number formatter,
which is pretty simple.
I've chosen to use a custom
format for my date formatter,
so I create a date
format from template,
with a simple template,
month, day, hour and minute,
and then I set that date format
that I got on my date formatter,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and it's ready to go
for my number formatter.
I just create it, and I set
an appropriate style on it.
There are also, if you want to
customize your number formatter,
there are many, many
different options,
so you can control
all the various pieces
of number formatting.
The only thing I'm doing here
is saying I don't want floats.
So, I set that up at the
beginning of my application,
and then I have my date
formatter and number formatter
that I can use whenever I have
to format one of these headers.
Now, one thing I did
want to keep in mind is
that the user might change their
locale while my app is running,
and if they do that, then I'm
stuck with this number formatter
or date formatter that I
created for the old locale.
So, I want to listen
to a notification,
a little further down, I'm going
to add myself as an observer
to the locale that changed
notification, and when I get
that notification, I'm just
going to recreate my formatters
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
with whatever the new locale is.
So, now my app should be ready.
Let's try running it in English,
and I see that I got
properly formatted numbers
and dates for English.
I also note that my title of the
window is changing appropriately
with my using string stick to
give appropriate representations
for the proper pluralization,
and so my numbers are formatted
using the appropriate thousand
separator for English, and
I get a suitable date format
and number format for English
showing first the month name
and day, followed by the
time in 12 hour format.
So, now if I go into
the system preferences,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
I change my primary language
to English, and my region
from English to German, and my
region format also to Germany.
Let's try running the
app again in German.
Of course, the interface is
localized to German, let's see,
and now we can see that I
get appropriately formatted
for German with the
right thousand separator,
rather than a comma, and
appropriately formatted dates
with a day number, month name,
and using 24 hour
format for the hours.
And, we can see also that
are string stick is giving us
appropriately pluralized titles
as the number of notes changes.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So, that's using date
and number formatters
to get properly formatted
user representations
of dates and numbers.
Now, let me go back
to the slides,
and let me talk about text.
Now, there are many
applications that deal with text
in one form or another.
Maybe your app is representing
text to the user that it got
from some external source, or
maybe you're dealing with text
that the user entered
themselves, but in either case,
you need to deal with all
the kinds of writing systems
that the users are
going to want to see.
I have some examples here.
These are all writing
systems for which we have font
and input method support
on both iOS and OS X.
You want to be able
to handle them all.
Well, here's some simple
rules for doing that.
The first one is, use
Unicode and in particular,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
the NSString class, which is
our standard representation
of the Unicode string
for representing text.
A second one is that when you
want to analyze this text,
going into or do something
to it, iterate through it,
search for a substring,
sort it, etc.,
use some of the standard
NSString API's,
which are all Unicode savvy,
and will deal with text in many
in many different
writing systems,
and as much as possible, use
the standard system views
and controls for displaying that
text or for accepting input.
Let me go into that
in some more detail.
So, Unicode is a
standard that allows us
to encode essentially all of the
world's living writing systems,
most of its date blocks
in a single strength,
we use NSString as our standard
Unicode containing object,
and it exposes the
contents using the UTF 16
and coding format.
The thing I want you to
remember about using Unicode
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
to represent text, is that you
cannot treat what the user sees
as a character as
being something
that is fixed length in memory.
That's true no matter what
encoding form you're using
or what normalization
form you're using.
It's just a fundamental
property of Unicode.
So, what we recommend
that you do is not deal
with individual characters
in a string.
Instead, work with ranges
of characters in a string
or substrings of a string.
Let me give you some examples.
So, here I have a Chinese
character, a Korean syllable,
and a couple of emogies.
And their representations
numerically in UTF 16 or UTF 32,
you can see that they all have
different lengths no matter what
encoding formats you've used.
So, to avoid dealing
with all this complexity,
you can use standard API's,
like range of composed
character sequence at index,
so this will give you
the range within a string
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
of what is effectively
user visible character,
what we call a composed
character sequence
or sometimes a character
cluster for short.
So we use this API, it will give
you the range that corresponds
to what the user is seeing
as a character and respect
that range, so you don't
split these things up,
and end up with broken text.
If you need to go through
a string; maybe you need
to through it by
character cluster or by word
or by sentence or by paragraph,
we have a standard API for that.
Innumerate substrings and
range options using block.
Depending on what options you
pass in, you can through it
by character cluster,
by word sentence, etc.,
and then you give it a block,
and your block gets called
with the range of each
individual component.
For example, if you're
going through it
by user visible characters,
character clusters,
you use the NSString
enumeration
by composed character
sequences options,
and then your block will
be called successively
with the range and the string
corresponding to this character,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
whatever it may be,
whether it's short or long.
Likewise, if you're going
through it by words,
use NSString enumeration
by words,
and your block will be called
successfully with the range
of each word and the text notice
that words are not necessarily
going to be separated
by punctuation or white space.
If you need to search for a
substring within a string,
there's a standard
API for that too.
Range of string options,
range locale.
There are a number of different
options you can pass in for case
and sensitive searching,
die printing
and sensitive searching,
if you're searching
for something user provider,
there's a good change you may
want to use both of those.
You can search forwards
or backwards.
There's the also the
anchored search option.
If you pass that in, you're not
looking for the next instance
of the substring, you're looking
to see whether it's present
or not at the given
location you start at,
which is often useful too.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And, sorting.
If you need to sort strings in
such a way as to present them
to the user in sorted order,
you have to keep in mind
that each different
language; not just language,
but each country or region,
may have a different standard,
sort order, in which they
like to see strings presented.
Some die critics are sensitive,
important, sometimes not,
even what constitutes a
letter for sorting may change,
and if you don't put
things in the right order,
people are not going to be able
to find what they're
looking for.
Now, there are a couple of API's
that you might look at for this.
NSString has a standard
compare method
and a localized standard
compare, so the difference is
that the compare method
is that locale and variant
for internal machinery
purposes for sorting.
That's not what you want to use
for user presentation of text.
For user presentation of text,
use localized standard compare.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
That will be locale sensitive.
It will give you the
order that users expect.
It will match, for
example, the order of files
that are sorted in the finder.
So, let me give you
a few examples.
Here is a set of strings.
Here's the order that
you would get them
if you sorted them according
to the locale independent
internal compare method.
If we change this to localized
standard compare for let us say,
the US English locale,
you notice that the
order changes a fair bit.
If we were using
Danish sort order,
it changes even still further,
or we're using a
Chinese sort order,
it changes quite a bit again.
So, use the standard API,
and you'll get the sort
order users expect;
whatever it may happen to be.
For displaying text, keep
in mind that the characters
as they appear in the string
are not necessarily directly
correlated with what
gets shown on the screen.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So, what gets shown on the
screen is a sequence of glifs.
A glif is the smallest unit from
a font that can be displayed.
It may represent one character
or more than one character
or a part of a character.
The mapping between the
characters in memory
and the glifs that are shown
is not necessarily simple
and general, it can mean many
to many, and even the ordering
of glifs within a line
can become complex.
Now, our standard views and
controls use the text system
to get proper Unicode
layout and display,
so use them as much as possible.
If you need to go beyond that
and do custom display,
use text API's.
Now it used to be, this was more
difficult on iOS than on OS X,
because on iOS, you had
to go down all the way
to the core text level to do it.
Not anymore.
With the iOS 7, we
have text kit API's,
and there are some
excellent sessions on this.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
There was an introductory
session yesterday.
There is a more advanced
session this afternoon
that will tell you exactly how
they keep track of the mapping
between the characters and
the glifs that display them.
Here's an example; I got
this from a developer.
This is some English text with
some Hebrew in the middle.
So, English goes left to right.
Hebrew goes right to left.
You put them together, you
have bidirectional text.
This is the proper order
for displaying the text,
but the way it shows up in
memory is very different.
It starts off with the English
on the left, flowing from left
to right, then the Hebrew
in logical order flows
for the first character,
which shows up as the
right-most one and so on.
Then this exclamation point;
it's a bit ambiguous whether
the explanation point goes
with the Hebrew or the English.
Here it goes with Hebrew.
In order to specify that,
there are some Unicode control
characters that are included
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
in this text and these
are things that may show
up in text that you get.
Text that you get from
an external source
or from your localizers, or
sometimes text from formatters
in bidirectional text languages.
It may include these to
specify certain cases
that are otherwise ambiguous.
And again, the text API's and
the standard system controls
and fields will handle
this properly,
and give you the appropriate
Unicode sensitive bidirectional
layout of this text.
Text input; it's
important to keep in mind
that it's not always a
matter of pressing a key
and getting a letter
in the text.
For languages that use complex
input methods like Chinese
and Japanese, usually
what will happen is
that the user types
a representation;
a phonetic representation
perhaps of the text they want,
that gets preliminarily
inserted into the text.
Then the system shows them
some options of choices,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and they pick the one
they actually want,
and after that gets confirmed,
and that's the final
text that goes in.
So, in this case, the text on
the left has been confirmed.
The underlying text;
it's called marked text,
is preliminary only, and then
they'll pick one, and again,
this is what it looks like OS X.
This is what is looks like
on iOS; the same text.
It's not just for
Chinese and Japanese too.
On OS X, if you press and hold
to get accenting characters,
it's the same, your preliminary
character gets insert,
then the user picks
the final one.
So, if you're dealing with text
as its input, keep in mind,
it's not just a simple matter of
being inserted letter by letter,
there may be this preliminary
marked test that shows
up in your text view first.
If you're dealing with text as
it changes, deal with it change
by change, not keystroke by
keystroke, and you probably want
to notice that text
views will tell you
if there's marked
text from where it is.
You probably don't want
to operate on that,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
because it's only preliminary,
it's not the final text.
One last thing to mention
is that names, addresses,
and phone numbers vary quite
a bit around the world.
Names use many different
writing systems;
sometimes a user will have
their family name first,
and first name last.
Sometimes they may
not have a family name
or not have a personal name.
Phone number formats
differ widely.
The number of digits used
and the punctuation that goes
between them, and there are
many different address formats
around the world.
So, try as much as possible
to avoid making assumptions
about these formats.
Do it only as your app needs
to for its own purposes.
One thing that can help
sometimes is data detectors,
which can detect phone
numbers and addresses
in many different international
formats, so you can add this
to some views, and it
will detect these things,
and even make them into links.
There is also NSDataDetector
at the foundation level
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
for doing this protomatically
[assumed spelling].
So, we have a lot of
documentation for all this stuff
on line, both conceptual
documentation, tutorials,
and there are a number
of relevant sessions
that you might be interested
in, including sessions
about auto layout, many
sessions about text kit
and text handling,
and tomorrow's very interesting
session on date and time.
So, the most important things
to take away; for localization,
you job is to make your
interfaces localizable,
preferably by using base
localization with auto layout,
and for your strings and code,
use NSLocalizedString
and gen strings.
For locale data, be sure to
use formatters; date formatters
and number formatters
when you're presenting
these things to the user.
With the constants or templates,
if necessary, customize them,
and then as calendar for
calendrical calculations.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
For text, use Unicode and NS
string, and the NSString API's
for iteration, searching,
sorting, and so forth,
and as much as possible, use
the standard views and controls
for input and display.
If you have to go beyond those,
use the text system API's
to do so, and get proper Unicode
aware layout and display.
Thanks everyone, have
a great conference.
[ Applause ]