localizing mobile apps

Post on 19-Jun-2015

1.319 Views

Category:

Technology

0 Downloads

Preview:

Click to see full reader

DESCRIPTION

English Slides for my talk originally given at Developer Week 2014 in Nuremberg, Germany. It covers internationalization and localization of mobile applications, using iOS as an example platform. I look at text, numbers, date and time, images and other resources, using correct plural forms and more. There is an example application available on GitHub: https://github.com/dschneller/I18N-Example

TRANSCRIPT

Localizing Mobile AppsDaniel Schneller, CenterDevice GmbH

AgendaI18N vs. L10N

Languages and Regions

Text

Date and Time

Numbers

Images and other Resources

https://github.com/dschneller/I18N-Example

I18N vs. L10N

I18N

L10N

I — 18 Characters — N

L-10 Characters-N

InternationalizationI — 18 Characters — N

LocalizationL-10 Characters-N

Internationalization

Internationalization[…] process of designing a software application so that it

can potentially be adapted to various languages andregions without engineering changes. […]*

* Wikipedia

Localization

Localization[…] the process of adapting internationalized software fora specific region or language by adding locale-specific

components and translating text. […]*

* Wikipedia

I18N L10NApp LocalizedApp

!"#$%&!

! 🌐 🌐 !"#

$%&

Languages and Regions

Languages and RegionsLanguage ≠ Region

12h time used by an American living in Germany

„Jänner“ – „Januar“ [German in Austria vs. in Germany for January]

Localization ≠ Locale German user might prefer English user interface texts

But: 24h time display

Languages and Regions

Languages and Regions

Languages and Regions

Languages and RegionsScheme Launch Arguments

-AppleLanguages (ar,de,fr)

Order determines preference

-AppleLocale en_US

NSLocaleEncapsulates Region Settings

+ (id)autoupdatingCurrentLocale+ (id)currentLocale

NSCurrentLocaleDidChangeNotification [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(localeDidChange:) name:NSCurrentLocaleDidChangeNotification object:nil]

Refresh formatters, caches NSLocale instances

Refresh screen content

NSLocale-[NSLocale objectForKey:]

NSString* NSLocaleIdentifier NSString* NSLocaleMeasurementSystem

NSString* NSLocaleLanguageCode NSString* NSLocaleDecimalSeparator

NSString* NSLocaleCountryCode NSString* NSLocaleGroupingSeparator

NSString* NSLocaleScriptCode NSString* NSLocaleCurrencySymbol

NSString* NSLocaleVariantCode NSString* NSLocaleExemplarCharacterSet

NSString* NSLocaleCurrencyCode NSString* NSLocaleCollatorIdentifier

NSString* NSLocaleCalendar NSString* NSLocaleQuotationBeginDelimiterKey

NSString* NSLocaleCollationIdentifier NSString* NSLocaleQuotationEndDelimiterKey

NSString* NSLocaleUsesMetricSystem

NSLocaleDoes not contain the current language!

Locale ≠ Localization [NSBundle mainBundle].localizations; // NSArray, (all)

[NSBundle mainBundle].preferredLocalizations[0]; // (current)

Caveats Localizations of InfoPlist.strings determine content of „localizations“

Intersected with user’s region preferences in Settings.app

AppleLanguages launch parameter

Text

Text ≠ TextLanguage of messages, buttons, labels etc. Writing direction & alignment Writing systems (Latin, Hebrew, Arabic…) Numbers in text Names & addresses Phrases, idioms and terminology Plurals Sorting …

Text ≠ TextLanguage of messages, buttons, labels etc. Writing direction & alignment Writing systems (Latin, Hebrew, Arabic…) Numbers in text Names & addresses Phrases, idioms and terminology Plurals Sorting …

Base Localization

Base LocalizationOne leading (base) language

Base LocalizationOne leading (base) language

Add Localization

Base LocalizationOne leading (base) language

Add Localization

Base LocalizationOne leading (base) language

Add Localization

“.strings” files

Base LocalizationOne leading (base) language

Add Localization

“.strings” filesgenstrings

Base LocalizationOne leading (base) language

Add Localization

“.strings” filesgenstrings

ibtool

Base LocalizationOne leading (base) language

Add Localization

“.strings” filesgenstrings

ibtool

One copy per language

Base LocalizationOne leading (base) language

Add Localization

“.strings” filesgenstrings

ibtool

One copy per language“.lproj” Ordner

Base LocalizationEine Leitsprache (Base)

“.strings” Files genstrings

ibtool

Eine Kopie pro Sprache “.lproj” Ordner

Base LocalizationEine Leitsprache (Base)

“.strings” Files genstrings

ibtool

Eine Kopie pro Sprache “.lproj” Ordner

Base LocalizationEine Leitsprache (Base)

“.strings” Files genstrings

ibtool

Eine Kopie pro Sprache “.lproj” Ordner

Base Localization…

/* Class = "IBUILabel"; text = "Loaded by SecondViewController"; ObjectID = "NDk-cv-Gan"; */

"NDk-cv-Gan.text" = "Loaded by SecondViewController";

"

/* Class = "IBUILabel"; text = "First View"; ObjectID = "KQZ-1w-vlD"; */

"KQZ-1w-vlD.text" = "First View";

Base LocalizationPreview in Assistant Editor

Xcode 6: Pick Language

Double-Length Pseudo-Language

Auto Layout

NSLocalizedStringFamily of macros

NSLocalizedStringNSLocalizedStringFromTableNSLocalizedStringFromTableInBundleNSLocalizedStringWithDefaultValue

Access “.strings” files

Utilize NSBundle -[[NSBundle mainBundle] localizedStringForKey:value:table:]

NSLocalizedStringExample

[button setTitle:NSLocalizedString(@"reset.counter.button.title", @"Reset Counter action button") forState:…];

genstrings — Localizable.strings /* Reset Counter action button */"reset.counter.button.title" = "Zurücksetzen";

.strings FilesUpdates?

.strings FilesUpdates?

Use ibtool every time you update your labels and text.In the Base.lproj folder:ibtool ChangedNib.xib --generate-strings-file NewStrings.stringsOpen the generated output file and copy all new string entries to ChangedNib.strings in each lproj.

#fail

Do not normalize text

Do not normalize textDRY! — Do Repeat Yourself

[NSString stringWithFormat:@”…”]

Do not normalize textDRY! — Do Repeat Yourself

[NSString stringWithFormat:@”…”]

Localizable.strings“count” = “Anzahl”

“game” = “Spiel”

“reset” = “zurücksetzen”

“save” = “sparen”

“thumbnail” = “Daumennagel”

Do not normalize textDRY! — Do Repeat Yourself

[NSString stringWithFormat:@”…”]

Localizable.strings“count” = “Anzahl”

“game” = “Spiel”

“reset” = “zurücksetzen”

“save” = “sparen”

“thumbnail” = “Daumennagel”

“reset count”

”zurücksetzen Anzahl”

“save game”

“sparen Spiel”

Do not normalize text

Do not normalize textContext and grammar are lost

Do not normalize textContext and grammar are lost

Structure of sentences

Do not normalize textContext and grammar are lost

Structure of sentences

Declination & conjugation …

Do not normalize textContext and grammar are lost

Structure of sentences

Declination & conjugation …

Use dedicated strings per use case

Do not normalize textContext and grammar are lost

Structure of sentences

Declination & conjugation …

Use dedicated strings per use case“reset.counter.button” = “Auf 0 stellen”

Do not normalize textContext and grammar are lost

Structure of sentences

Declination & conjugation …

Use dedicated strings per use case“reset.counter.button” = “Auf 0 stellen”

Context for translators

Do not normalize textContext and grammar are lost

Structure of sentences

Declination & conjugation …

Use dedicated strings per use case“reset.counter.button” = “Auf 0 stellen”

Context for translatorsUse self explanatory keys

Do not normalize textContext and grammar are lost

Structure of sentences

Declination & conjugation …

Use dedicated strings per use case“reset.counter.button” = “Auf 0 stellen”

Context for translatorsUse self explanatory keys

Provide useful comments (button vs. label, approx. length, etc.)

VariablesParameters

[NSString stringWithFormat: NSLocalizedString(@"click.counter.label", @"P1: Current Click Count."), self.clickCount]

"

/* P1: Current Click Count. */"click.counter.label" = "%1d x geklickt”;

VariablesDocument parameter order!

“Zeige 4 von 12 gesamt”

“Total 12 — Showing 4”

/* … Param 1: current page; Param 2: total count */"current.page.label" = “Zeige %1d von %2d gesamt”;/* … Param 1: current page; Param 2: total count */"current.page.label" = “Total %2d — Showing %1d”;

Special cases: 0 and 1

Plurals

Englisch German

0 No books Keine Bücher1 One book Ein Buch

sonstiges 100 books 100 Bücher

Localizable.strings“books.0” = “No Books”

“books.1” = “One Book”

“books.n” = “%1d books”

“books.0” = “Keine Bücher”

“books.1” = “Ein Buch”

“books.n” = “%1d Bücher”

If-else-clause in the code: Problem solved.

Englisch Deutsch

0 No books Keine Bücher1 1 book 1 Buch

Vielleicht nicht ganz…

Plural

Englisch Deutsch

0 No books Keine Bücher1 1 book 1 Buch2 2 books 2 Bücher

wenige 3 books 3 Bücherviele 11 books 11 Bücher

sonstiges 100 books 100 Bücher

Vielleicht nicht ganz…

Plural

0

1

2

wenige

viele

sonstiges

Englisch

No books1 book2 books3 books11 books100 books

German

Keine Bücher1 Buch

2 Bücher3 Bücher

11 Bücher100 Bücher

Or is it…?

Plural

0

1

2

wenige

viele

sonstiges

Englisch

No books1 book2 books3 books11 books100 books

German

Keine Bücher1 Buch

2 Bücher3 Bücher

11 Bücher100 Bücher

Arabic

٠ كتابكتابكتابان

٣ كتب١١ كتابًا١٠٠ كتاب

Or is it…?

Plural

.strings + .stringsdict

* http://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html#ar

.strings + .stringsdictAvailable since iOS7

* http://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html#ar

.strings + .stringsdictAvailable since iOS7

Implements (Unicode*) localization rules forPlural

Gender [rdar://16670931]

* http://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html#ar

.strings + .stringsdictAvailable since iOS7

Implements (Unicode*) localization rules forPlural

Gender [rdar://16670931]

Plist FormatName must match .strings

.strings must exist, but may be empty

* http://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html#ar

.strings + .stringsdict

https://developer.apple.com/library/ios/releasenotes/Foundation/↩︎ RN-Foundation/index.html#//apple_ref/doc/uid/TP30000742-CH2-SW56

<dict>    <key>files.selected.label.%d</key>    <dict>        <key>NSStringLocalizedFormatKey</key> <string>%#@num_files_are@ selected</string>        <key>num_files_are</key>  <dict>            <key>NSStringFormatSpecTypeKey</key> <string>NSStringPluralRuleType</string>            <key>NSStringFormatValueTypeKey</key> <string>d</string>            <key>zero</key>    <string>No file is</string>            <key>one</key>     <string>A file is</string>            <key>other</key>   <string>%d files are</string>        </dict>    </dict></dict>

.stringsdictCategories per language according to Unicode

iOS additionally supports „zero“ category for all languages

Others, e. g. „few“, depend on language

String TablesLocalizable.strings

Default used by NSLocalizedString()

Big projects — big file

Can be split NSLocalizedStringFromTable(“MyController”, “Key”, “Comment”)

MyController.strings

Project structure — Context for translators

3rd party code

Also works for .stringsdict

Date and Time

Date and Time NSDateFormatter

Date and TimeTransforms between NSDate and NSString

+[NSDateFormatter localizedStringFromDate:dateStyle:timeStyle:] -[NSDateFormatter setLocale:]

NSDateFormatter

Date and TimeTransforms between NSDate and NSString

+[NSDateFormatter localizedStringFromDate:dateStyle:timeStyle:] -[NSDateFormatter setLocale:]

NSDateFormatterStyle

NSDateFormatter

Date and TimeTransforms between NSDate and NSString

+[NSDateFormatter localizedStringFromDate:dateStyle:timeStyle:] -[NSDateFormatter setLocale:]

NSDateFormatterStyle

German / Germany Englisch / USA

NSDateFormatter

Date and TimeTransforms between NSDate and NSString

+[NSDateFormatter localizedStringFromDate:dateStyle:timeStyle:] -[NSDateFormatter setLocale:]

NSDateFormatterStyle

German / Germany Englisch / USA

Short 08.07.14 22:32 7/8/14,10:32 PM

NSDateFormatter

Date and TimeTransforms between NSDate and NSString

+[NSDateFormatter localizedStringFromDate:dateStyle:timeStyle:] -[NSDateFormatter setLocale:]

NSDateFormatterStyle

German / Germany Englisch / USA

Short 08.07.14 22:32 7/8/14,10:32 PM

Medium 08.07.2014 22:32:36 Jul 8, 2014, 10:32:36 PM

NSDateFormatter

Date and TimeTransforms between NSDate and NSString

+[NSDateFormatter localizedStringFromDate:dateStyle:timeStyle:] -[NSDateFormatter setLocale:]

NSDateFormatterStyle

German / Germany Englisch / USA

Short 08.07.14 22:32 7/8/14,10:32 PM

Medium 08.07.2014 22:32:36 Jul 8, 2014, 10:32:36 PM

Long 8. Juli 2014 22:32:36 MESZ Jul 8, 2014, 10:32:36 PM GMT+2

NSDateFormatter

Date and TimeTransforms between NSDate and NSString

+[NSDateFormatter localizedStringFromDate:dateStyle:timeStyle:] -[NSDateFormatter setLocale:]

NSDateFormatterStyle

German / Germany Englisch / USA

Short 08.07.14 22:32 7/8/14,10:32 PM

Medium 08.07.2014 22:32:36 Jul 8, 2014, 10:32:36 PM

Long 8. Juli 2014 22:32:36 MESZ Jul 8, 2014, 10:32:36 PM GMT+2

Full Dienstag, 8. Juli 2014 12:32:36 Mitteleuropäische Sommerzeit

Tuesday, July 8, 2014 at 12:39:16 PM Central European Summer Time

NSDateFormatter

Date and TimeTransforms between NSDate and NSString

+[NSDateFormatter localizedStringFromDate:dateStyle:timeStyle:] -[NSDateFormatter setLocale:]

NSDateFormatterStyle

German / Germany Englisch / USA

Short 08.07.14 22:32 7/8/14,10:32 PM

Medium 08.07.2014 22:32:36 Jul 8, 2014, 10:32:36 PM

Long 8. Juli 2014 22:32:36 MESZ Jul 8, 2014, 10:32:36 PM GMT+2

Full Dienstag, 8. Juli 2014 12:32:36 Mitteleuropäische Sommerzeit

Tuesday, July 8, 2014 at 12:39:16 PM Central European Summer Time

No - -

NSDateFormatter

Date and TimeNSDateFormatter.h

typedef enum { NSDateFormatterNoStyle = …, NSDateFormatterShortStyle = …, NSDateFormatterMediumStyle = …, NSDateFormatterLongStyle = …, NSDateFormatterFullStyle = …} NSDateFormatterStyle

Combine for date and time portions NSDateFormatterNoStyle — only date / time

Get updated with the OS

NSDateFormatter

Date and Time NSDateFormatter

* http://www.unicode.org/reports/tr35/tr35-dates.html#Contents

Date and TimeWhat if defaults are not suitable?

NSDateFormatter

* http://www.unicode.org/reports/tr35/tr35-dates.html#Contents

Date and TimeWhat if defaults are not suitable?

Do not use hard coded format strings!

NSDateFormatter

* http://www.unicode.org/reports/tr35/tr35-dates.html#Contents

Date and TimeWhat if defaults are not suitable?

Do not use hard coded format strings!

Unicode Locale Data Markup Language*

NSDateFormatter

* http://www.unicode.org/reports/tr35/tr35-dates.html#Contents

Date and TimeWhat if defaults are not suitable?

Do not use hard coded format strings!

Unicode Locale Data Markup Language*[f setDateFormat:[NSDateFormatter dateFormatFromTemplate:@"u QQ" options:0 locale:LOCALE_EN_US]];NSLog(@“%@", [f stringFromDate:july8th);

NSDateFormatter

* http://www.unicode.org/reports/tr35/tr35-dates.html#Contents

Date and TimeWhat if defaults are not suitable?

Do not use hard coded format strings!

Unicode Locale Data Markup Language*[f setDateFormat:[NSDateFormatter dateFormatFromTemplate:@"u QQ" options:0 locale:LOCALE_EN_US]];NSLog(@“%@", [f stringFromDate:july8th);

Q3 2014

NSDateFormatter

* http://www.unicode.org/reports/tr35/tr35-dates.html#Contents

Relative formatting [formatter setDoesRelativeDateFormatting:YES];NSLog(@“%@, %@, %@…", [formatter stringFromDate:GESTERN], [formatter stringFromDate:HEUTE], [formatter stringFromDate:MORGEN]);

Gestern, Heute, Morgen… [de_DE]

Yesterday, Today, Tomorrow… [en_US]

Yesterday, Today, Tomorrow…

NSDateComponentsFormatter Formats durations

[f stringFromTimeInterval:1234.0]) // seconds

About 20 minutes remaining

NSDateIntervalFormatter Formats time intervals

[f stringFromDate:NOW toDate:LATER])

09.07.14 12:10-13:13

Coming up next: iOS8

Numbers

Numbers

Numbers

“Plain” Numbers

Numbers

MassDistance

LengthCurrency

PercentageData Volume

WeightEnergy

Spelled Out

“Plain” Numbers

NumbersDecimal separators

Grouping characters

Currency symbols

"

NSNumberFormatter API similar to NSDateFormatter

Numbers NSNumberFormatter

German / Germany English / USA

No 1234,56 1234.56

Decimal 1.234,56 1,234.56

Currency 1.234,56 € $1,234.56

Percent 123.456% 123,456%

Scientific 1,23456E+03 1.23456E3

SpellOut eintausendzweihundertvier-unddreißig Komma fünf sechs

one thousand two hundred thirty-four point five six

NumbersNSNumberFormatter.h

enum { NSNumberFormatterNoStyle = …, NSNumberFormatterDecimalStyle = …, NSNumberFormatterCurrencyStyle = …, NSNumberFormatterPercentStyle = …, NSNumberFormatterScientificStyle = …, NSNumberFormatterSpellOutStyle = …} typedef NSUInteger NSNumberFormatterStyle

Numbers NSNumberFormatter

NumbersNSNumberFormatter* f = [[NSNumberFormatter alloc] init];f.numberStyle = NSNumberFormatterNoStyle;

f.locale = LOCALE_DE_AT; NSLog(@"NoStyle de_AT: %@", [f stringFromNumber:@(1234.56)]);

f.locale = LOCALE_EN_US; f.maximumFractionDigits = 2;NSLog(@“NoStyle en_US: %@", [f stringFromNumber:@(1234.56)]);

NSNumberFormatter

NumbersNSNumberFormatter* f = [[NSNumberFormatter alloc] init];f.numberStyle = NSNumberFormatterNoStyle;

f.locale = LOCALE_DE_AT; NSLog(@"NoStyle de_AT: %@", [f stringFromNumber:@(1234.56)]);

f.locale = LOCALE_EN_US; f.maximumFractionDigits = 2;NSLog(@“NoStyle en_US: %@", [f stringFromNumber:@(1234.56)]);

NoStyle de_AT: 1235

NoStyle en_US: 1234.56

NSNumberFormatter

NumbersMany more customization options

maximumFractionDigits

usesSignificantDigits

minimum/maximumSignificantDigits

paddingCharacter

roundingMode, roundingIncrement

NSNumberFormatter

Data VolumeNSByteCountFormatter

File size, amount of memory

Picks suitable unit automatically

Non-numeric display

Allow Non-Numeric Zero KB

File 1,234.57 GB

Memory 1,149.78 GB

Coming up next: iOS8NSEnergyFormatter

Energy in joule, calories etc.

NSLengthFormatter Distance, in miles, kilometers etc.

NSMassFormatter Mass and weight in pounds, kg etc.

Images and other Resources

ImagesButtons

UI Elements

UI Elements-[UIImage imageNamed:@"myImage"];

Put image files into .lproj folders

Does not work for asset catalogues

Launch ImageDisable asset catalogue

Launch images in .lproj folders

Follows usual naming conventions for Retina

Orientation

iPhone vs. iPad

Other RessourcesSame as images

Lookup via -[[NSBundle mainBundle] pathForResource:ofType:]

Use cases HTML

Text files

App NameInfo.plist

Application has localized Display Name

<key>LSHasLocalizedDisplayName</key><true />

InfoPlist.strings "CFBundleDisplayName" = "国际范例";

Wrap up

Cover the BasicsLocale based formatting

NSDateFormatter

NSNumberFormatter

NSByteCountFormatter

Careful and diligent translation Remember resources besides the source code

Next StepsImages resources

Launch Images (if relevant)

Addresses, names

Right-to-left support

Location based pre-selection of units (miles, km etc.)

Pre-selection for pickers, options etc.

Color scheme

SummaryInitialer effort can be significant

Full translation

Code refactoring

Create workflows and pick tools

Ongoing maintenance rather easy

Consider relevance of individual measures for your audience

Merci!

Hvala!Vielen Dank!

Thank You!

Grazie!

Questions?

Daniel Schneller — @dschneller daniel.schneller@centerdevice.com

top related