Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Локализации iOS-приложений (Я.Субботник, Минск,...
Search
Andrey Subbotin
June 04, 2012
Technology
100
1
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Локализации iOS-приложений (Я.Субботник, Минск, 2 июня 2012 г.)
Andrey Subbotin
June 04, 2012
More Decks by Andrey Subbotin
See All by Andrey Subbotin
Hello world: что кроется за поворотом
eploko
0
44
Автоматизация локализации iOS-приложений
eploko
1
410
Other Decks in Technology
See All in Technology
Genie Codeハンズオン応用編
taka_aki
0
130
三人寄ればチューリング完全
puhitaku
4
1.6k
FORENSIA: ローカルLLMフォレンジックハーネス
sumeshi
2
430
LLM Internals: 언어 모델의 계보와 알고리즘 진화 (2023~2026)
inureyes
PRO
1
800
コーチングの奥義 何もしないテクニック
jinwatanabe
0
140
Kiro Crew入門 - 常駐エージェントの仕組みと使いどころ / Intro to Kiro Crew
k_adachi_01
1
660
AI開発に用いられるHPC技術について
gpuunite_official
0
190
英語が話せなくてもKubeConスタッフに参加した話
yusuke427
0
1k
強化学習「理論」入門
enakai00
3
3.6k
My broken English still works: speaking at global OSS events
naruoga
0
120
研究開発部の紹介 / Sansan R&D Profile
sansan33
PRO
4
24k
Sets in Go
ramalho
1
1.2k
Featured
See All Featured
Marketing Yourself as an Engineer | Alaka | Gurzu
gurzu
0
280
Technical Leadership for Architectural Decision Making
baasie
3
510
<Decoding/> the Language of Devs - We Love SEO 2024
nikkihalliwell
1
290
Amusing Abliteration
ianozsvald
1
260
Tell your own story through comics
letsgokoyo
1
1k
Connecting the Dots Between Site Speed, User Experience & Your Business [WebExpo 2025]
tammyeverts
11
1k
Money Talks: Using Revenue to Get Sh*t Done
nikkihalliwell
0
470
Groundhog Day: Seeking Process in Gaming for Health
codingconduct
0
290
The Psychology of Web Performance [Beyond Tellerrand 2023]
tammyeverts
49
3.5k
How to Grow Your eCommerce with AI & Automation
katarinadahlin
PRO
1
250
Designing Powerful Visuals for Engaging Learning
tmiket
1
500
Put a Button on it: Removing Barriers to Going Fast.
kastner
60
4.5k
Transcript
Я.Субботник, Минск, 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'] end 34
One command to rule them all! 35 $ rake babelyoda
$ rake -T rake babelyoda rake babelyoda:create_keysets rake babelyoda:drop_empty_strings rake
babelyoda:drop_orphan_keys rake babelyoda:drop_orphan_keysets rake babelyoda:extract rake babelyoda:extract_strings rake babelyoda:extract_xib_strings rake babelyoda:fetch_strings rake babelyoda:initBabelfile rake babelyoda:localize_xibs rake babelyoda:pull rake babelyoda:push rake babelyoda:remote:drop_keysets rake babelyoda:remote:list rake 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 Английский текст в качестве ключа WelcomeButtonTitle WelcomeTitle ButtonTitleWelcome WelcomeTITLE
WelcomeBtnTitle
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]; ɿ̑ਓ
69 Склеивание строк NSLocalizedString( @"People in the room: %[one, some,
many, none]d", @"Blah blah"); ɿ̑ਓ
WTF!? 70
71 WTF!?
72 WTF!?
73 WTF!?
74 WTF!?
75 WTF!? WTF!? WTF!?
76 WTF!?
77 WTF!? WTF!?
Разработчик, Мобильные Яндекс.Карты (iOS)
[email protected]
@eploko Андрей Субботин