Slide 1

Slide 1 text

Localization done bien Trainline × CocoaHeads Paris, April 2018

Slide 2

Slide 2 text

Localisation done bien Trainline × CocoaHeads Paris, April 2018

Slide 3

Slide 3 text

DISCLAIMER This talk will be given in fr_FR and en_MX

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

14 languages "#$%&'( )*+,-./

Slide 6

Slide 6 text

"#$%&'( )*+,-./ 14 locales

Slide 7

Slide 7 text

[correct] localisation? → Travellers won’t buy domestic tickets if they don’t feel the app is domestic → Conversion rate is higher if localised in customer’s language → Bad localisation is worse than no localisation

Slide 8

Slide 8 text

Localisation Internationalisation Code → Developer Words → Translator

Slide 9

Slide 9 text

Make your app feel so native in any language that people think it has been written in their own language.

Slide 10

Slide 10 text

It is difficult

Slide 11

Slide 11 text

What is a locale? [language] + [formatting conventions] ON A [territory] USING A [codeset] Date Time Numbers Currencies Mexico France Great Britain ES FR NL EN Netherlands UTF-8 Durations UTF-16

Slide 12

Slide 12 text

# 1 en_US Luggage en_GB Baggage Target tourists that probably have never traveled by train Target commuters going to London It’s not a matter of languages…

Slide 13

Slide 13 text

) 2 nl_NL nl_BE …or of countries alone 2 fr_BE " fr_FR 3 fr_CA 3 en_CA 1 en_US

Slide 14

Slide 14 text

Grammar ru_RU has 4 different forms of plural is_IS has 15 to 20 indefinite articles

Slide 15

Slide 15 text

ahh, formatting too 4 # " jueves, 12 de abril 7:15 PM $ 4602 MXN es_MX en_GB jeudi 12 avril 19h15 204 € fr_FR Thursday 12 April 19:15 £178

Slide 16

Slide 16 text

and let’s not forget about typography ¿Cómo podemos ayudarte? Comment pouvons-nous vous aider ? & "

Slide 17

Slide 17 text

and let’s not forget about typography « Je recommande l’usage de Trainline, surtout pour les entreprises ayant plusieurs centaines de voyages tous les mois. » Tarif : PROMOTION STANDARD " “I definitely recommend using Trainline for Business for all companies that book hundreds of trips per month.” Refund and exchange: PROMOTION STANDARD #

Slide 18

Slide 18 text

No content

Slide 19

Slide 19 text

No content

Slide 20

Slide 20 text

No content

Slide 21

Slide 21 text

No content

Slide 22

Slide 22 text

⚙ Verifying configuration... 7 Running with required locales ["zh-Hans", "cs", "es", "nb", "de", "en", "it", "pl", "pt-PT", "nl", "sv", "fr", "pt-BR", "da"] 8 Finding string keys required by scanning app source code and xibs (shelling out)... 7 Fetching latest strings from remote service... 7 : Updating local strings tables... 7 ; No translation issues detected. Complete

Slide 23

Slide 23 text

__attribute__((annotate("returns_localized_nsstring"))) NSString *CATLocalized(NSString *key); NSString *CATLocalized(NSString *key, NSString *defaultText); NSString *CATLocalized(NSString *key, NSInteger count); NSString *CATLocalized(NSString *key, NSDictionary *substitutions); NSString *CATUnlocalized(NSString *);

Slide 24

Slide 24 text

CATLocalized(@"ui.ios.session.disconnected") __attribute__((annotate("returns_localized_nsstring"))) NSString *CATLocalized(NSString *key);

Slide 25

Slide 25 text

- (NSString *)nameForCarrier:(NSString *)carrier { NSString *key = [@"data.carrier." stringByAppendingString:self]; return CATLocalized(key.lowercaseString, carrier); } __attribute__((annotate("returns_localized_nsstring"))) NSString *CATLocalized(NSString *key, NSString *defaultText);

Slide 26

Slide 26 text

CATLocalized(@"ui.ios.exchange.passengerWarning", self.passengers.count) "You must select the passenger" "You must select the %{count} passengers" __attribute__((annotate("returns_localized_nsstring"))) NSString *CATLocalized(NSString *key, NSInteger count);

Slide 27

Slide 27 text

CATLocalized(@"ui.ios.profiles.addYouthAge", @{@"age":self.currentProfile.ageDescription}) __attribute__((annotate("returns_localized_nsstring"))) NSString *CATLocalized(NSString *key, NSDictionary *substitutions);

Slide 28

Slide 28 text

CATUnlocalized(@"https://192.168.0.1:3000") NSString *CATUnlocalized(NSString *); ⚠ Debug code only

Slide 29

Slide 29 text

>

Slide 30

Slide 30 text

Write code,
 need a new translation Create key on TMS
 with en_GB translation Request translation Run i18n script of project ⚙

Slide 31

Slide 31 text

(also, some stuff isn’t localised)

Slide 32

Slide 32 text

Some stuff isn’t localised and that’s okay

Slide 33

Slide 33 text

What should we take care of?

Slide 34

Slide 34 text

languages right-to-left Out of the box support from iOS 9 up

Slide 35

Slide 35 text

two rules of thumb:

Slide 36

Slide 36 text

first rule: Use leadingAnchor and trailingAnchor instead of leftAnchor and rightAnchor

Slide 37

Slide 37 text

second rule: The locale comes with a defined natural order (RTL or LTR)

Slide 38

Slide 38 text

and an exception… Override the right to left alignment on any specific text view

Slide 39

Slide 39 text

will you flip… or not? ✅ MOST CONTROLS Segmented controls Progress indicators Outline views Video/timeline indicators Images Clocks Music notes Graphs

Slide 40

Slide 40 text

Use more powerful file formats

Slide 41

Slide 41 text

better plurals handling .stringdicts to the rescue C

Slide 42

Slide 42 text

better plurals handling

Slide 43

Slide 43 text

better plurals handling The key to grab the translation on our code

Slide 44

Slide 44 text

better plurals handling

Slide 45

Slide 45 text

better plurals handling The value for the key and placeholder

Slide 46

Slide 46 text

better plurals handling The plural rules applying for the first declared variable

Slide 47

Slide 47 text

D ❓ ❓ ❓ ❓ ❓ ❓ ❓ ❓ ❓ ❓ ❓ ❓ ❓ ❓ ❓ ❓ ❓ ❓

Slide 48

Slide 48 text

xliff anyone? D

Slide 49

Slide 49 text

xliff anyone? D XML Localisation Interchange File Format

Slide 50

Slide 50 text

Xcode 9 goodies for localisation

Slide 51

Slide 51 text

property list view for plurals

Slide 52

Slide 52 text

test your localisations with ease

Slide 53

Slide 53 text

localise images

Slide 54

Slide 54 text

No content

Slide 55

Slide 55 text

Don’t us sentences as localization keys

Slide 56

Slide 56 text

Don’t hardcode string

Slide 57

Slide 57 text

Don’t concatenate strings

Slide 58

Slide 58 text

“Cancel inward of Adrien” ui.ticket.cancel + ticketPart + ui.of + passengerName "Cancel %{part} of %{passenger}" % « Rückfahrt von Adrien stornieren »

Slide 59

Slide 59 text

Don’t assume punctuations

Slide 60

Slide 60 text

Don’t use the same string in multiple places

Slide 61

Slide 61 text

# & Cancelar Cancel es_ES en_GB Anular Cancel action ticket

Slide 62

Slide 62 text

Don’t use reflexion

Slide 63

Slide 63 text

No content

Slide 64

Slide 64 text

Follow typographic rules

Slide 65

Slide 65 text

Take care of plurals

Slide 66

Slide 66 text

Departing on %{date}, one passenger Departing on %{date}, %{count} passengers

Slide 67

Slide 67 text

Use meaningful token names over position

Slide 68

Slide 68 text

The $1 card ending in $2 expires on $3 The %{type} card ending in %{lastDigit} expires on %{date}

Slide 69

Slide 69 text

Use unicode by default

Slide 70

Slide 70 text

Localise all the things including accessibility, server error messages, etc.

Slide 71

Slide 71 text

Cleanup useless locales

Slide 72

Slide 72 text

Questions? @adhumi & @vidriloco We are hiring an iOS lead