78
Я.Субботник, Минск, 2 июня 2012 года Разработчик, Мобильные Яндекс.Карты (iOS) Андрей Субботин Локализации iOS- приложений

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

  • Upload
    yandex

  • View
    2.368

  • Download
    1

Embed Size (px)

DESCRIPTION

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

Citation preview

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

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

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

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

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

Can't read,won't buy.

2

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

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

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

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

3

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

4

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

http://bit.ly/whylocalize

5

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

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

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

6

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

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

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

7

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

8

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

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

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

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

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

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

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

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

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

XIB-файлы.

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

9

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

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

10

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

= ваш друг!

11

NSLocalizedString

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

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

12

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

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

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

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

NSFormatter

Data Formatting Guide→

14

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

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

15

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

*.m → Localizable.strings

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

16

Page 17: Андрей Субботин "Локализация приложений для 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" = "Некий примерный текст";

17

ru.lproj/Localizable.strings

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

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

18

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

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

19

*.xib → *.strings

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

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

"8.normalTitle" = "Welcome!";

20

en.lproj/ViewController.strings

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

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

21

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

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

22

*.strings → *.xib

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

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

23

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

Создали en.XIB.

24

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

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

Page 25: Андрей Субботин "Локализация приложений для 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

25

en.xib → ru.xib

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

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

26

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

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

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

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

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

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

27

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

Tanker

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

28

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

Yoda

29

Babelfish

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

Babelyoda

30

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

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

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

31

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

в Tanker

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

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

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

PROFIT!!

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

Babelyoda

32

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

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

Babelfile

33

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

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

Page 34: Андрей Субботин "Локализация приложений для 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']end34

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

One command to rule them all!

35

$ rake babelyoda

Page 36: Андрей Субботин "Локализация приложений для 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

36

Page 37: Андрей Субботин "Локализация приложений для 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

37

yxbuildkit-prebuild.sh

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

http://bit.ly/yandextool

38

Available on GitHub!

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

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

хоррор стори

39

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

I scanned 12 directories.

40

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

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

41

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

I scanned 1 directories.

42

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

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

43

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

I scanned 1 directory.

44

Page 45: Андрей Субботин "Локализация приложений для 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”) );

45

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

46

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

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

47

"I scanned %g %@."

"directory"

"directories"

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

"папка"

"каталоги"

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

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

48

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

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

49

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

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

50

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

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

51

Page 52: Андрей Субботин "Локализация приложений для 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];

52

Page 53: Андрей Субботин "Локализация приложений для 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; } }

53

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

54

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

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

55

"%d change"

"%d changes"

"%d changes"

"%d changes"

Page 56: Андрей Субботин "Локализация приложений для 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];

56

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

57

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

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

58

"NumberChanges0"

"NumberChanges3"

"NumberChanges2"

"NumberChanges1"

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

genstrings “магия”

59

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

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

60

Page 61: Андрей Субботин "Локализация приложений для 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";

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" = "";

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

62

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

"%[some]d changes"

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

“Хитрости”

63

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

64

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

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

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

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

65

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

WelcomeButtonTitleWelcomeTitleButtonTitleWelcomeWelcomeTITLEWelcomeBtnTitle

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

66

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

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

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

67

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

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

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

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

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

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人

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

69

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

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

へやへ:5人

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

WTF!?

70

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

71

WTF!?

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

72

WTF!?

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

73

WTF!?

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

74

WTF!?

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

75

WTF!?

WTF!?

WTF!?

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

76

WTF!?

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

77WTF!? WTF!?

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

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

[email protected]

@eploko

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