Лучшая практика использования NSLocalizedString

Я (как и все остальные), используя NSLocalizedString, чтобы локализовать мое приложение.

К сожалению, существует несколько "недостатков" (не обязательно ошибка самой NSLocalizedString), включая

  • Нет автозаполнения для строк в Xcode. Это делает работу не только подверженной ошибкам, но и утомительной.
  • Вы можете в конечном итоге переопределить строку просто потому, что не знали, что уже существует эквивалентная строка (например, "Пожалуйста, введите пароль" или "Введите пароль сначала" )
  • Сходным образом с проблемой автозаполнения вам нужно "запомнить" /copypaste строки комментариев, иначе genstring будет содержать несколько комментариев для одной строки
  • Если вы хотите использовать genstring после того, как вы уже локализовали некоторые строки, вы должны быть осторожны, чтобы не потерять старые локализации.
  • Те же строки разбросаны по всему вашему проекту. Например, вы использовали NSLocalizedString(@"Abort", @"Cancel action") всюду, а затем Code Review попросит вас переименовать строку в NSLocalizedString(@"Cancel", @"Cancel action"), чтобы сделать код более последовательным.

Что я делаю (и после некоторых поисков в SO, которые, как я понял, многие люди делают это), должен иметь отдельный файл strings.h, где я #define весь код локализации. Например

// In strings.h
#define NSLS_COMMON_CANCEL NSLocalizedString(@"Cancel", nil)
// Somewhere else
NSLog(@"%@", NSLS_COMMON_CANCEL);

Это, по сути, обеспечивает завершение кода, одно место для изменения имен переменных (так что больше не требуется генерации) и уникальное ключевое слово для авторефрактора. Однако это связано с тем, что заканчивается целым рядом операторов #define, которые по своей сути не структурированы (например, LocString.Common.Cancel или что-то в этом роде).

Итак, хотя это работает несколько отлично, мне было интересно, как вы, ребята, делаете это в своих проектах. Существуют ли другие подходы к упрощению использования NSLocalizedString? Может быть, даже инфраструктура, которая инкапсулирует его?

Ответ 1

NSLocalizedString имеет несколько ограничений, но он настолько важен для Cocoa, что необоснованно писать собственный код для обработки локализации, то есть вам придется его использовать. Тем не менее, небольшая помощь может помочь, вот как я продолжаю:

Обновление файла строк

genstrings перезаписывает ваши строковые файлы, отбрасывая все ваши предыдущие переводы. Я написал update_strings.py для анализа старого файла строк, запустите genstrings и заполните пробелы, чтобы вам не пришлось вручную восстанавливать ваши существующие переводы. script пытается максимально совместить существующие строковые файлы, чтобы избежать слишком большой разницы при их обновлении.

Именование ваших строк

Если вы используете NSLocalizedString как описано:

NSLocalizedString(@"Cancel or continue?", @"Cancel notice message when a download takes too long to proceed");

Вы можете определить одну и ту же строку в другой части вашего кода, которая может конфликтовать, поскольку один и тот же английский термин может иметь разное значение в разных контекстах (OK и Cancel). Вот почему я всегда использую бессмысленную строку all-caps с префиксом, специфичным для модуля, и очень точное описание:

NSLocalizedString(@"DOWNLOAD_CANCEL_OR_CONTINUE", @"Cancel notice window title when a download takes too long to proceed");

Использование одной и той же строки в разных местах

Если вы используете одну и ту же строку несколько раз, вы можете либо использовать макрос, как и вы, либо кэшировать его как переменную экземпляра в вашем контроллере представления или источнике данных. Таким образом, вам не придется повторять описание, которое может стать устаревшим и несовместимым между экземплярами одной и той же локализации, что всегда сбивает с толку. Поскольку переменные экземпляра являются символами, вы сможете использовать автозаполнение для этих наиболее распространенных переводов и использовать "ручные" строки для конкретных, которые будут возникать только один раз.

Я надеюсь, что вы будете более продуктивны при локализации Cocoa с этими советами!

Ответ 2

Что касается autocompletition для строк в Xcode, вы можете попробовать http://questbe.at/lin/.

Ответ 3

Согласитесь с ndfred, но я хотел бы добавить это:

Второй параметр может использоваться как... значение по умолчанию!!

(NSLocalizedStringWithDefaultValue работает неправильно с genstring, поэтому я предложил это решение)

Вот моя пользовательская реализация, использующая NSLocalizedString, которая использует комментарий как значение по умолчанию:

1. В предварительно скомпилированном заголовке (файл .pch) переопределите макрос "NSLocalizedString":

// cutom NSLocalizedString that use macro comment as default value
#import "LocalizationHandlerUtil.h"

#undef NSLocalizedString
#define NSLocalizedString(key,_comment) [[LocalizationHandlerUtil singleton] localizedString:key  comment:_comment]

2. создать класс для реализации обработчика локализации

#import "LocalizationHandlerUtil.h"

@implementation LocalizationHandlerUtil

static LocalizationHandlerUtil * singleton = nil;

+ (LocalizationHandlerUtil *)singleton
{
    return singleton;
}

__attribute__((constructor))
static void staticInit_singleton()
{
    singleton = [[LocalizationHandlerUtil alloc] init];
}

- (NSString *)localizedString:(NSString *)key comment:(NSString *)comment
{
    // default localized string loading
    NSString * localizedString = [[NSBundle mainBundle] localizedStringForKey:key value:key table:nil];

    // if (value == key) and comment is not nil -> returns comment
    if([localizedString isEqualToString:key] && comment !=nil)
        return comment;

    return localizedString;
}

@end

3. Используйте его!

Убедитесь, что вы добавили Run script на фазах создания приложений, чтобы файл Localizable.strings обновлялся в каждой сборке, т.е. в файле Localized.strings будет добавлена ​​новая локализованная строка:

Моя фаза сборки script представляет собой оболочку script:

Shell: /bin/sh
Shell script content: find . -name \*.m | xargs genstrings -o MyClassesFolder

Итак, когда вы добавляете эту новую строку в свой код:

self.title = NSLocalizedString(@"view_settings_title", @"Settings");

Затем выполните сборку, ваш файл. /Localizable.scripts будет содержать следующую строку:

/* Settings */
"view_settings_title" = "view_settings_title";

И поскольку значение key == для 'view_settings_title', пользовательский LocalizedStringHandler вернет комментарий, т.е. "Настройки"

Voilà: -)

Ответ 4

Я написал script, чтобы помочь поддерживать Localizable.strings на нескольких языках. Хотя это не помогает в autocompletion, это помогает объединить файлы .strings с помощью команды:

merge_strings.rb ja.lproj/Localizable.strings en.lproj/Localizable.strings

Подробнее см.: https://github.com/hiroshi/merge_strings

Некоторые из вас считают это полезным, надеюсь.

Ответ 5

В Swift я использую следующее, например. для кнопки "Да" в этом случае:

NSLocalizedString("btn_yes", value: "Yes", comment: "Yes button")

Обратите внимание на использование value: для текстового значения по умолчанию. Первым параметром служит идентификатор перевода. Преимущество использования параметра value: заключается в том, что текст по умолчанию может быть изменен позже, но идентификатор перевода остается неизменным. Файл Localizable.strings будет содержать "btn_yes" = "Yes";

Если параметр value: не использовался, то первый параметр будет использоваться для обоих: для идентификатора перевода, а также для текстового значения по умолчанию. Файл Localizable.strings будет содержать "Yes" = "Yes";. Этот вид управления файлами локализации кажется странным. Особенно, если переведенный текст длинный, тогда идентификатор длинный. Всякий раз, когда изменяется какой-либо символ текстового значения по умолчанию, меняется и идентификатор перевода. Это приводит к проблемам при использовании внешних систем перевода. Изменение идентификатора перевода понимается как добавление нового текста перевода, который может не всегда желать.

Ответ 6

#define PBLocalizedString(key, val) \

[[NSBundle mainBundle] localizedStringForKey:(key) value:(val) table:nil]

Ответ 7

с iOS 7 и Xcode 5, вам следует избегать использования метода Localization.strings и использовать новый метод "базовой локализации". Есть несколько учебных пособий, если вы google для "базовой локализации"

Apple doc: Базовая локализация

Ответ 8

Я сам часто увлекаюсь кодированием, забывая помещать записи в файлы .strings. Таким образом, у меня есть вспомогательные скрипты, чтобы найти, что я должен вернуть в файлы .strings и перевести.

Поскольку я использую свой собственный макрос над NSLocalizedString, , пожалуйста, просмотрите и обновите script перед использованием, как я предположил для простоты, что nil используется в качестве второго параметра для NSLocalizedString. Часть, которую вы хотите изменить, -

NSLocalizedString\(@(".*?")\s*,\s*nil\) 

Просто замените его тем, что соответствует вашему макросу и использованию NSLocalizedString.

Здесь находится script, вам действительно нужна только часть 3. Остальное - легче увидеть, где все это происходит:

// Part 1. Get keys from one of the Localizable.strings
perl -ne 'print "$1\n" if /^\s*(".+")\s*=/' myapp/fr.lproj/Localizable.strings

// Part 2. Get keys from the source code
grep -n -h -Eo -r  'NSLocalizedString\(@(".*?")\s*,\s*nil\)' ./ | perl -ne 'print "$1\n" if /NSLocalizedString\(@(".+")\s*,\s*nil\)/'

// Part 3. Get Part 1 and 2 together.

comm -2 -3 <(grep -n -h -Eo -r  'NSLocalizedString\(@(".*?")\s*,\s*nil\)' ./ | perl -ne 'print "$1\n" if /NSLocalizedString\(@(".+")\s*,\s*nil\)/' | sort | uniq) <(perl -ne 'print "$1\n" if /^\s*(".+")\s*=/' myapp/fr.lproj/Localizable.strings | sort) | uniq >> fr-localization-delta.txt

Выходной файл содержит ключи, которые были найдены в коде, но не в файле Localizable.strings. Вот пример:

"MPH"
"Map Direction"
"Max duration of a detailed recording, hours"
"Moving ..."
"My Track"
"New Trip"

Конечно, можно отполировать больше, но думал, что я поделюсь.

Ответ 9

Если кто-то ищет решение Swift. Вы можете проверить мое решение, которое я собрал здесь: SwiftyLocalization

С помощью нескольких шагов для настройки у вас будет очень гибкая локализация в Google Spreadsheet (комментарий, пользовательский цвет, выделение, шрифт, несколько листов и т.д.).

Короче говоря, шаги: Google Spreadsheet → Файлы CSV → Localizable.strings

Кроме того, он также создает Localizables.swift, структуру, которая действует как интерфейсы для поиска и декодирования ключей для вас (вы должны вручную указать способ декодирования строки из ключа, хотя).

Почему это здорово?

  • Вам больше не нужно иметь ключ как обычную строку по всем местам.
  • Неверные ключи обнаруживаются во время компиляции.
  • Xcode может выполнять автозаполнение.

Пока есть инструменты, которые могут автозаполнять ваш локализуемый ключ. Ссылка на реальную переменную гарантирует, что она всегда является допустимым ключом, иначе она не будет компилироваться.

// It defined as computed static var, so it up-to-date every time you call. 
// You can also have your custom retrieval method there.

button.setTitle(Localizables.login.button_title_login, forState: .Normal)

В проекте используется приложение Google Script для преобразования таблиц → CSV и Python Script для преобразования файлов CSV → Localizable.strings. Вы можете быстро просмотреть этот примерный лист, чтобы узнать, что возможно.