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


Я (как и все другие) с помощью NSLocalizedStringдля локализации моего приложения.

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

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

то, что я делаю (и после некоторых поисков на так я понял, что многие люди делают это), чтобы иметь отдельный где #define все локализации-код. Например

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

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

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

9 127

9 ответов:

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

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

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

именования строк

если вы используете 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");

используя ту же строку в разных местах

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

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

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

согласен с ndfred, но я хотел бы добавить следующее:

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

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

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

1 . В заранее скомпилированных заголовков (.PCH file), переопределить '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. Используй его!

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

мой сценарий фазы сборки-это сценарий оболочки:

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

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

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

затем выполните сборку, ваш ./Локализуемый.файл скриптов будет содержать эту новую строку:

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

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

вуаля :-)

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

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

подробнее: https://github.com/hiroshi/merge_strings

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

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

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

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

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

#define PBLocalizedString(key, val) \

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

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

Apple doc:базовый локализации

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

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

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

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

вот идет скрипт, вам нужна только Часть 3 действительно. Остальное, чтобы увидеть легче, где все это происходит:

// Part 1. Get keys from one of the Localizable.strings
perl -ne 'print "\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 "\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 "\n" if /NSLocalizedString\(@(".+")\s*,\s*nil\)/' | sort | uniq) <(perl -ne 'print "\n" if /^\s*(".+")\s*=/' myapp/fr.lproj/Localizable.strings | sort) | uniq >> fr-localization-delta.txt

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

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

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

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

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

короче говоря, шаги: Google Spreadsheet -- > CSV-файлы -- > локализуемые.строки

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

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

  1. вам больше не нужно иметь ключ в виде простой строки во всех местах.
  2. во время компиляции обнаруживаются неправильные ключи.
  3. Xcode может сделать автозаполнение.

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

// It's defined as computed static var, so it's 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 скрипт для преобразования листы --> CSV и скрипт Python, чтобы преобразовать CSV-файлов --> локализации.строки вы можете быстро взглянуть на этот примерный лист, чтобы узнать, что возможно.