Андрей Субботин "Локализация приложений для iOS: как...

Preview:

DESCRIPTION

Ужасы локализации и как с ними бороться на пошаговом примере: от «Эврика, нам нужно перевести проект на язык Х!» до «Как не прострелить себе ногу, когда у вас есть Xcode, разработчики, переводчики и дедлайн». Рассматриваются все базовые инструменты локализации (genstrings, ibtool) в целях понимания, как их использовать с наименьшими телодвижениями. Отдельно рассказывается про то, как мы подружили весь этот «зоопарк» в Яндексе и чем готовы поделиться.

Citation preview

Я.Субботник, Минск, 2 июня 2012 года

Разработчик, Мобильные Яндекс.Карты (iOS)Андрей Субботин

Локализации iOS-приложений

Can't read,won't buy.

2

52,4% не покупают продукт на чужом языке.

60% — для Франции, Японии и России.

89,3% — если английский знают плохо.

3

4

http://bit.ly/whylocalize

5

Интернационализация i18n

= подготовка продукта к локализации

6

Локализация L10n

= адаптация продукта к конкретному языку и местности

7

8

Локализация L10n

Понятный язык интерфейса.

Дата и время в привычном формате.

Корректная сортировка списков.

Поддержка местных единиц измерения.

Правильное форматирование чисел.

Что локализуется в приложении?

Текстовые строки.

XIB-файлы.

Изображения, аудио.

9

Подготовка строк к локализации

10

= ваш друг!

11

NSLocalizedString

NSLog(NSLocalizedString(@"Some sample text", @"A text string to be output to the logs."));

12

NSLog(NSLocalizedString(@"Some sample text", @"A text string to be output to the logs."));

13

2012-03-06 08:10:05.433 L10nSample[15433:f803] Some sample text

2012-03-06 08:11:02.117 L10nSample[15438:f903] Некий примерный текст

= тоже ваш друг!

NSFormatter

Data Formatting Guide→

14

Выделение строк

15

*.m → Localizable.strings

$ genstrings *.m -o Resources/en.lproj

16

en.lproj/Localizable.strings

/* A text string to be output to the logs. */

"Some sample text" = "Some sample text";

/* A text string to be output to the logs. */

"Some sample text" = "Некий примерный текст";

17

ru.lproj/Localizable.strings

Выделение строк из XIB

18

$ ibtool --export-strings-file \en.lproj/ViewController.strings \en.lproj/ViewController.xib

19

*.xib → *.strings

/* Class = "IBUIButton"; normalTitle = "Welcome!"; ObjectID = "8"; */

"8.normalTitle" = "Welcome!";

20

en.lproj/ViewController.strings

Вмерживание переводов обратно

21

$ ibtool --import-strings-file \en.lproj/ViewController.strings \en.lproj/ViewController.xib \--write en.lproj/ViewController.xib

22

*.strings → *.xib

Инкрементальное обновление XIB'ов

23

Создали en.XIB.

24

А теперь что?!

Локализовали en.XIB → ru.XIB.Добавили новую кнопку в en.XIB.

$ ibtool--previous-file en.lproj/Window.old.xib --incremental-file ru.lproj/Window.old.xib --strings-file ru.lproj/Window.strings --localize-incremental--write ru.lproj/Window.xib en.lproj/Window.new.xib

25

en.xib → ru.xib

Храните предыдущиеверсии XIB файлов.

26

Не правьте рукамилокализованные XIB файлы.

ПереводчикиПонимают английский.

Не всегда понимают русский.

Не используют Xcode и не редактируют XIB.

Не знают контекста переводабез вашей помощи.

27

Tanker

= web-сервис= API для загрузки и выгрузки переводов

28

Yoda

29

Babelfish

Babelyoda

30

избавляет отрутинной ручной работы

сводит количество багов при локализации к минимуму

31

генерирует .stringsиз кода и XIB’ов загружает .strings

в Tanker

забирает из Tanker’асвежие переводы

обновляетXIB файлы

аккуратно всекоммитит в git

PROFIT!!

Babelyoda

32

= библиотека для работы с .strings, genstrings и ibtool

Babelfile

33

...по аналогии с Makefile, Gemfile, Rakefile и т.п.

= единое место конфигурации.

Babelyoda::Specification.new do |s| s.name = 'YandexMaps'

s.development_language = :en s.localization_languages = [:ru, :uk, :tr]

s.engine = Babelyoda::Tanker.new do |t| t.token = ENV['TANKER_TOKEN'] t.project_id = 'myak_iphone' t.endpoint = ENV['TANKER_HOST'] end

s.scm = Babelyoda::Git.new

s.source_files = FileList['{Classes,Shared}/**/*.{m,mm,h}'] s.resources_folder = 'Resources' s.xib_files = FileList['Resources/**/en.lproj/*.xib'] s.strings_files = FileList['Resources/**/en.lproj/*.strings']end34

One command to rule them all!

35

$ rake babelyoda

$ rake -Trake babelyodarake babelyoda:create_keysetsrake babelyoda:drop_empty_stringsrake babelyoda:drop_orphan_keysrake babelyoda:drop_orphan_keysetsrake babelyoda:extractrake babelyoda:extract_stringsrake babelyoda:extract_xib_stringsrake babelyoda:fetch_stringsrake babelyoda:initBabelfilerake babelyoda:localize_xibsrake babelyoda:pullrake babelyoda:pushrake babelyoda:remote:drop_keysetsrake babelyoda:remote:listrake babelyoda:verify

36

#!/bin/bash

function verify { if [ $CONFIGURATION == 'AppStore' ] ; then rvm rvmrc trust . && rvm rvmrc load . && bundle \ && bundle exec rake babelyoda:verify return $? fi return 0}git submodule update --init --recursive && verify

37

yxbuildkit-prebuild.sh

http://bit.ly/yandextool

38

Available on GitHub!

Плюрализация

хоррор стори

39

I scanned 12 directories.

40

NSLog(@"I scanned %g directories.", directoryCount);

41

I scanned 1 directories.

42

NSLog(@"I scanned %g %@.", directoryCount, directoryCount == 1 ? @"directory" : @"directories", );

43

I scanned 1 directory.

44

NSLog( NSLocalizedString(@"I scanned %g %@.", @”Text to show the number of directories scanned”), dirScanCount, dirScanCount == 1 ? NSLocalizedString(@"directory", @”Single directory”) : NSLocalizedString(@"directories", @”Plural directories”) );

45

46

Как это видит переводчик?

47

"I scanned %g %@."

"directory"

"directories"

"Я просканировал %g %@."

"папка"

"каталоги"

Я отсканировал 1 папка.

48

Я отсканировал 5 каталоги.

49

NSLog( dirScanCount == 1 ? NSLocalizedString("I scanned %g directory.", @”Blah”) : NSLocalizedString("I scanned %g directories.", @”Blah”), dirScanCount );

50

NSString *pluralTransfers = NSLocalizedString(@"%d changes", @"The number of changes shown in the route description");

51

NSString *forms[4] = {0};forms[0] = NSLocalizedString(@"%d change", @"Blah");forms[1] = NSLocalizedString(@"%d changes", @"Blah");forms[2] = NSLocalizedString(@"%d changes", @"Blah");forms[3] = NSLocalizedString(@"%d changes", @"Blah"); int form = YXPluralFormForN(self.transfersCount);NSString *pluralTransfers = forms[i];

52

int YXPluralFormForRU(NSInteger n){ // One - 1, 21, 31, ... // Some - 2-4, 22-24, 32-34 ... // Many - 5-20, 25-30, ... NSInteger n10 = n % 10; if ((n10 == 1) && ((n == 1) || (n > 20))) { return 0; } else if ((n10 > 1) && (n10 < 5) && ((n > 20) || (n < 10))) { return 1; } else { return 2; } }

53

54

Как это видит переводчик?

55

"%d change"

"%d changes"

"%d changes"

"%d changes"

NSString *forms[4] = {0};forms[0] = NSLocalizedString(@"NumberChanges0", @"Blah");forms[1] = NSLocalizedString(@"NumberChanges1", @"Blah");forms[2] = NSLocalizedString(@"NumberChanges2", @"Blah");forms[3] = NSLocalizedString(@"NumberChanges3", @"Blah"); int form = YXPluralFormForN(self.transfersCount);NSString *pluralTransfers = forms[i];

56

57

Как это видит переводчик?

58

"NumberChanges0"

"NumberChanges3"

"NumberChanges2"

"NumberChanges1"

genstrings “магия”

59

NSLocalizedString(@"%[one, some, many, none]d changes", @"The number of changes shown in the route description");

60

/* The number of changes shown in the route description */"%[one]d changes" = "%d changes";"%[some]d changes" = "%d changes";"%[many]d changes" = "%d changes";"%[none]d changes" = "%d changes";

61

Localizable.strings

/* The number of changes shown in the route description */"%[one]d changes" = "%d остановка";"%[some]d changes" = "%d остановки";"%[many]d changes" = "%d остановок";"%[none]d changes" = "";

62

"%[one, some, many, none]d changes"

"%[some]d changes"

“Хитрости”

63

64

Английский текст в качестве ключа

NSLocalizedString(@"Tap Here", @"Action button title");

NSLocalizedString(@"TapButtonTitle", @"Action button title");

65

Английский текст в качестве ключа

WelcomeButtonTitleWelcomeTitleButtonTitleWelcomeWelcomeTITLEWelcomeBtnTitle

66

Различные контексты

Edit = ПравитьEdit = ИзменитьEdit = Переименовать

67

Различные контексты

NSLocalizedStringFromTable(<#key#>, <#tbl#>, <#comment#>)

NSLocalizedStringFromTable(@”Edit”,@”Common”,@”Blah”)

NSLocalizedStringFromTable(@”Edit”,@”Buttons”,@”Blah”)

68

Склеивание строк

NSString *part1 = NSLocalizedString(@"People in the room", @"Part 1");NSString *part2 = NSLocalizedString(@"%d", @"Part 2");NSString *halfResult = [NSString stringWithFormat:@"%@: %@", part1, part2];NSString *result = [NSString stringWithFormat:halfResult, 5];

へやへ:5人

69

Склеивание строк

NSLocalizedString( @"People in the room: %[one, some, many, none]d", @"Blah blah");

へやへ:5人

WTF!?

70

71

WTF!?

72

WTF!?

73

WTF!?

74

WTF!?

75

WTF!?

WTF!?

WTF!?

76

WTF!?

77WTF!? WTF!?

Разработчик, Мобильные Яндекс.Карты (iOS)

eploko@yandex-team.ru

@eploko

Андрей Субботин

Recommended