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

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

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

  • нет autocompletition для строки в Xcode. Это делает работу не только подвержено ошибкам, но и утомительно.
  • вы можете в конечном итоге переопределить строку просто потому, что вы не знали, что эквивалентная строка уже существует (например, "введите пароль" против " введите пароль первый")
  • аналогично вопросу автозаполнения, вам нужно "запомнить" / copypaste строки комментариев, иначе 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 ответов


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

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

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

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


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


согласен с 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. Используй его!

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

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

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

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

вуаля :-)


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

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";. Этот вид управления файлами локализации кажется странным. Особенно если переведенный текст длинный, то id долгим. Всякий раз, когда какой-либо символ текстового значения по умолчанию изменяется, идентификатор перевода также изменяется. Это приводит к проблемам при использовании внешних систем перевода. Изменение идентификатора перевода понимается как добавление нового перевода текст, который может быть не всегда желателен.


#define PBLocalizedString(key, val) \

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

с iOS 7 & в Xcode 5, Вы должны избегать использования локализации.strings' метод, и использовать новый метод "базовой локализации". Есть несколько учебных пособий, если вы 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-файлы --> локализуемые.струны

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

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

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

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

// 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 App для преобразования листов --> CSV и скрипт Python для преобразования файлов CSV --> Localizable.строки вы можете быстро взглянуть на этот пример листа, чтобы узнать, что возможно.