Андрей Субботин "Автоматизация локализации...

121
1 Автоматизация локализации iOS-приложений Андрей Субботин [email protected] @eploko

description

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

Transcript of Андрей Субботин "Автоматизация локализации...

Page 1: Андрей Субботин "Автоматизация локализации iOS-приложений"

1

Автоматизациялокализации iOS-приложений

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

[email protected]@eploko

Page 2: Андрей Субботин "Автоматизация локализации iOS-приложений"

2

Зачем?

Как это? Кто?

Что это?

Яндекс. Страшное

Полезное Забавное

Page 3: Андрей Субботин "Автоматизация локализации iOS-приложений"

3

Зачем нужна локализация?

Page 4: Андрей Субботин "Автоматизация локализации iOS-приложений"

4

Can't read,won't buy.

Page 5: Андрей Субботин "Автоматизация локализации iOS-приложений"

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

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

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

5

Page 6: Андрей Субботин "Автоматизация локализации iOS-приложений"

http://bit.ly/whylocalize

6

Page 7: Андрей Субботин "Автоматизация локализации iOS-приложений"

7

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

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

Page 8: Андрей Субботин "Автоматизация локализации iOS-приложений"

8

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

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

местности

Page 9: Андрей Субботин "Автоматизация локализации iOS-приложений"

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

9

Page 10: Андрей Субботин "Автоматизация локализации iOS-приложений"

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

10

Page 11: Андрей Субботин "Автоматизация локализации iOS-приложений"

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

11

Page 12: Андрей Субботин "Автоматизация локализации iOS-приложений"

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

12

Page 13: Андрей Субботин "Автоматизация локализации iOS-приложений"

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

13

Page 14: Андрей Субботин "Автоматизация локализации iOS-приложений"

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

14

Page 15: Андрей Субботин "Автоматизация локализации iOS-приложений"

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

15

Page 16: Андрей Субботин "Автоматизация локализации iOS-приложений"

XIB-файлы.

16

Page 17: Андрей Субботин "Автоматизация локализации iOS-приложений"

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

17

Page 18: Андрей Субботин "Автоматизация локализации iOS-приложений"

Как приложение подгружает ресурсы?

18

Page 19: Андрей Субботин "Автоматизация локализации iOS-приложений"

19

Page 20: Андрей Субботин "Автоматизация локализации iOS-приложений"

en.lproj

20

Page 21: Андрей Субботин "Автоматизация локализации iOS-приложений"

ru.lproj

21

Page 22: Андрей Субботин "Автоматизация локализации iOS-приложений"

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

22

Page 23: Андрей Субботин "Автоматизация локализации iOS-приложений"

= ваш друг!

23

NSLocalizedString

Page 24: Андрей Субботин "Автоматизация локализации iOS-приложений"

NSLocalizedString(@"key", @"translator comment")

24

Page 25: Андрей Субботин "Автоматизация локализации iOS-приложений"

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

25

Page 26: Андрей Субботин "Автоматизация локализации iOS-приложений"

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

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

26

Page 27: Андрей Субботин "Автоматизация локализации iOS-приложений"

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

27

NSFormatter

→ Data Formatting Guide

Page 28: Андрей Субботин "Автоматизация локализации iOS-приложений"

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

28

Page 29: Андрей Субботин "Автоматизация локализации iOS-приложений"

*.m → Localizable.strings

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

29

Page 30: Андрей Субботин "Автоматизация локализации iOS-приложений"

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" = "Некий примерный текст";

30

ru.lproj/Localizable.strings

Page 31: Андрей Субботин "Автоматизация локализации iOS-приложений"

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

31

Page 32: Андрей Субботин "Автоматизация локализации iOS-приложений"

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

32

*.xib → *.strings

Page 33: Андрей Субботин "Автоматизация локализации iOS-приложений"

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

"8.normalTitle" = "Welcome!";

33

en.lproj/ViewController.strings

Page 34: Андрей Субботин "Автоматизация локализации iOS-приложений"

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

34

Page 35: Андрей Субботин "Автоматизация локализации iOS-приложений"

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

35

*.strings → *.xib

Page 36: Андрей Субботин "Автоматизация локализации iOS-приложений"

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

36

Page 37: Андрей Субботин "Автоматизация локализации iOS-приложений"

Создали en.XIB.

37

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

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

Page 38: Андрей Субботин "Автоматизация локализации iOS-приложений"

$ 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

38

en.xib → ru.xib

Page 39: Андрей Субботин "Автоматизация локализации iOS-приложений"

$ 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

39

en.xib → ru.xib

Page 40: Андрей Субботин "Автоматизация локализации iOS-приложений"

$ 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

40

en.xib → ru.xib

Page 41: Андрей Субботин "Автоматизация локализации iOS-приложений"

$ 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

41

en.xib → ru.xib

Page 42: Андрей Субботин "Автоматизация локализации iOS-приложений"

$ 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

42

en.xib → ru.xib

Page 43: Андрей Субботин "Автоматизация локализации iOS-приложений"

$ 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

43

en.xib → ru.xib

Page 44: Андрей Субботин "Автоматизация локализации iOS-приложений"

$ 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

44

en.xib → ru.xib

Page 45: Андрей Субботин "Автоматизация локализации iOS-приложений"

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

45

Page 46: Андрей Субботин "Автоматизация локализации iOS-приложений"

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

46

Page 47: Андрей Субботин "Автоматизация локализации iOS-приложений"

Переводчики

47

Page 48: Андрей Субботин "Автоматизация локализации iOS-приложений"

понимают английский.

48

Переводчики

Page 49: Андрей Субботин "Автоматизация локализации iOS-приложений"

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

49

Переводчики

Page 50: Андрей Субботин "Автоматизация локализации iOS-приложений"

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

50

Переводчики

Page 51: Андрей Субботин "Автоматизация локализации iOS-приложений"

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

51

Переводчики

Page 52: Андрей Субботин "Автоматизация локализации iOS-приложений"

Как ониработают?

52

Page 53: Андрей Субботин "Автоматизация локализации iOS-приложений"

но редко.

53

Вместе с вами,

Page 54: Андрей Субботин "Автоматизация локализации iOS-приложений"

Очень часто

54

по e-mail.

Page 55: Андрей Субботин "Автоматизация локализации iOS-приложений"

+1 к карме

55

translations.launchpad.net

Page 56: Андрей Субботин "Автоматизация локализации iOS-приложений"

56

Tanker

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

Page 57: Андрей Субботин "Автоматизация локализации iOS-приложений"

Yoda

57

Babelfish

Page 58: Андрей Субботин "Автоматизация локализации iOS-приложений"

Babelyoda

58

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

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

минимуму

Page 59: Андрей Субботин "Автоматизация локализации iOS-приложений"

59

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

в Tanker

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

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

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

PROFIT!!

Page 60: Андрей Субботин "Автоматизация локализации iOS-приложений"

Babelyoda

60

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

Page 61: Андрей Субботин "Автоматизация локализации iOS-приложений"

Babelfile

61

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

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

Page 62: Андрей Субботин "Автоматизация локализации iOS-приложений"

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']end

62

Page 63: Андрей Субботин "Автоматизация локализации iOS-приложений"

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']end

63

Page 64: Андрей Субботин "Автоматизация локализации iOS-приложений"

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']end

64

Page 65: Андрей Субботин "Автоматизация локализации iOS-приложений"

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']end

65

Page 66: Андрей Субботин "Автоматизация локализации iOS-приложений"

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']end

66

Page 67: Андрей Субботин "Автоматизация локализации iOS-приложений"

One command to rule them all!

67

$ rake babelyoda

Page 68: Андрей Субботин "Автоматизация локализации iOS-приложений"

$ 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

68

Page 69: Андрей Субботин "Автоматизация локализации iOS-приложений"

#!/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

69

yxbuildkit-prebuild.sh

Page 70: Андрей Субботин "Автоматизация локализации iOS-приложений"

https://github.com/eploko/babelyoda

70

Available on GitHub!

Page 71: Андрей Субботин "Автоматизация локализации iOS-приложений"

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

71

хоррор стори

Page 72: Андрей Субботин "Автоматизация локализации iOS-приложений"

I scanned 12 directories.

72

Page 73: Андрей Субботин "Автоматизация локализации iOS-приложений"

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

73

Page 74: Андрей Субботин "Автоматизация локализации iOS-приложений"

I scanned 1 directories.

74

Page 75: Андрей Субботин "Автоматизация локализации iOS-приложений"

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

75

Page 76: Андрей Субботин "Автоматизация локализации iOS-приложений"

I scanned 1 directory.

76

Page 77: Андрей Субботин "Автоматизация локализации iOS-приложений"

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

77

Page 78: Андрей Субботин "Автоматизация локализации iOS-приложений"

78

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

Page 79: Андрей Субботин "Автоматизация локализации iOS-приложений"

79

"I scanned %g %@."

"directory"

"directories"

Page 80: Андрей Субботин "Автоматизация локализации iOS-приложений"

80

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

"папка"

"каталоги"

Page 81: Андрей Субботин "Автоматизация локализации iOS-приложений"

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

81

Page 82: Андрей Субботин "Автоматизация локализации iOS-приложений"

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

82

Page 83: Андрей Субботин "Автоматизация локализации iOS-приложений"

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

83

Page 84: Андрей Субботин "Автоматизация локализации iOS-приложений"

“It is more complicated than you think.”— The Eighth Networking Truth, from RFC 1925

84

Page 85: Андрей Субботин "Автоматизация локализации iOS-приложений"

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

85

Page 86: Андрей Субботин "Автоматизация локализации iOS-приложений"

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];

86

Page 87: Андрей Субботин "Автоматизация локализации iOS-приложений"

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; } }

87

Page 88: Андрей Субботин "Автоматизация локализации iOS-приложений"

88

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

Page 89: Андрей Субботин "Автоматизация локализации iOS-приложений"

89

"%d change"

"%d changes"

"%d changes"

"%d changes"

Page 90: Андрей Субботин "Автоматизация локализации iOS-приложений"

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];

90

Page 91: Андрей Субботин "Автоматизация локализации iOS-приложений"

91

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

Page 92: Андрей Субботин "Автоматизация локализации iOS-приложений"

92

"NumberChanges0"

"NumberChanges3"

"NumberChanges2"

"NumberChanges1"

Page 93: Андрей Субботин "Автоматизация локализации iOS-приложений"

genstrings “магия”

93

Page 94: Андрей Субботин "Автоматизация локализации iOS-приложений"

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

94

Page 95: Андрей Субботин "Автоматизация локализации iOS-приложений"

/* 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";

95

Localizable.strings

Page 96: Андрей Субботин "Автоматизация локализации iOS-приложений"

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

96

Localizable.strings

Page 97: Андрей Субботин "Автоматизация локализации iOS-приложений"

NSString *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 @”[one]”; } else if ((n10 > 1) && (n10 < 5) && ((n > 20) || (n < 10))) { return @”[some]”; } else { return @”[many]”; } }

97

Page 98: Андрей Субботин "Автоматизация локализации iOS-приложений"

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

NSString *pluralTransfers = YXLocalizedStringN(pluralKey, self.transfersCount);

98

Page 99: Андрей Субботин "Автоматизация локализации iOS-приложений"

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

"%[some]d changes"

Page 100: Андрей Субботин "Автоматизация локализации iOS-приложений"

“Хитрости”

100

Page 101: Андрей Субботин "Автоматизация локализации iOS-приложений"

101

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

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

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

Page 102: Андрей Субботин "Автоматизация локализации iOS-приложений"

102

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

WelcomeButtonTitleWelcomeTitleButtonTitleWelcomeWelcomeTITLEWelcomeBtnTitle

Page 103: Андрей Субботин "Автоматизация локализации iOS-приложений"

103

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

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

Page 104: Андрей Субботин "Автоматизация локализации iOS-приложений"

104

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

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

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

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

Page 105: Андрей Субботин "Автоматизация локализации iOS-приложений"

105

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

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人

Page 106: Андрей Субботин "Автоматизация локализации iOS-приложений"

106

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

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

へやへ:5人

Page 107: Андрей Субботин "Автоматизация локализации iOS-приложений"

107

Полезные проекты

Page 108: Андрей Субботин "Автоматизация локализации iOS-приложений"

108

Linguan

Page 109: Андрей Субботин "Автоматизация локализации iOS-приложений"

109

Wincent Strings Utility

“Merges, extracts and combines .string files (for incremental localization)”— http://wincent.com/a/products/wincent-strings-util/

Page 110: Андрей Субботин "Автоматизация локализации iOS-приложений"

110

genstrings2

“40x faster than genstrings”— http://www.cocoanetics.com/2012/01/genstrings2/

Page 111: Андрей Субботин "Автоматизация локализации iOS-приложений"

111

Twine

“String Management for iOS, Mac OS X, and Android Development”— http://www.mobiata.com/blog/2012/02/08/twine-string-management-ios-mac-os-x

Page 112: Андрей Субботин "Автоматизация локализации iOS-приложений"

112

WTF!?

Page 113: Андрей Субботин "Автоматизация локализации iOS-приложений"

113

WTF!?

Page 114: Андрей Субботин "Автоматизация локализации iOS-приложений"

114

WTF!?

Page 115: Андрей Субботин "Автоматизация локализации iOS-приложений"

115

WTF!?

Page 116: Андрей Субботин "Автоматизация локализации iOS-приложений"

116

WTF!?

Page 117: Андрей Субботин "Автоматизация локализации iOS-приложений"

117

WTF!?

WTF!?

WTF!?

Page 118: Андрей Субботин "Автоматизация локализации iOS-приложений"

118

WTF!?

Page 119: Андрей Субботин "Автоматизация локализации iOS-приложений"

119

WTF!?

WTF!?

WTF!?

WTF!?

WTF!?

Page 120: Андрей Субботин "Автоматизация локализации iOS-приложений"

120

WTF!? WTF!?

Page 121: Андрей Субботин "Автоматизация локализации iOS-приложений"

Andrey Subbotin

[email protected]

@eploko

Вопросы? :-)