Upload
yandex
View
2.368
Download
1
Embed Size (px)
DESCRIPTION
Ужасы локализации и как с ними бороться на пошаговом примере: от «Эврика, нам нужно перевести проект на язык Х!» до «Как не прострелить себе ногу, когда у вас есть Xcode, разработчики, переводчики и дедлайн». Рассматриваются все базовые инструменты локализации (genstrings, ibtool) в целях понимания, как их использовать с наименьшими телодвижениями. Отдельно рассказывается про то, как мы подружили весь этот «зоопарк» в Яндексе и чем готовы поделиться.
Citation preview
Я.Субботник, Минск, 2 июня 2012 года
Разработчик, Мобильные Яндекс.Карты (iOS)Андрей Субботин
Локализации iOS-приложений
Can't read,won't buy.
2
52,4% не покупают продукт на чужом языке.
60% — для Франции, Японии и России.
89,3% — если английский знают плохо.
3
4
Интернационализация 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
Андрей Субботин