Лучшая практика с использованием NSLocalizedString - PullRequest
133 голосов
/ 27 марта 2012

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

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

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

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

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

Ответы [ 9 ]

93 голосов
/ 17 апреля 2012

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

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

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

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

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

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

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

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

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

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

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

30 голосов
/ 28 сентября 2013

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

24 голосов
/ 09 мая 2012

Согласен с 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 на этапах сборки приложения, чтобы файл Localizable.strings обновлялся при каждой сборке, т. Е. Новая локализованная строка добавлялась в ваши Localized.strings.file:

Моя фаза сборки Script - это скрипт оболочки:

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

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

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

Затем выполните сборку, ваш.1035 * Вуаля: -)

3 голосов
/ 01 сентября 2017

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

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

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

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

2 голосов
/ 29 сентября 2013

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

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

Для получения дополнительной информации см .: https://github.com/hiroshi/merge_strings

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

0 голосов
/ 13 октября 2016

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

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

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

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

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

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

0 голосов
/ 07 апреля 2016

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

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

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

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

Здесь идет сценарий, вам нужна только часть 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"

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

0 голосов
/ 02 февраля 2014

в iOS 7 и Xcode 5 следует избегать использования метода «Localization.strings» и использовать новый метод «базовой локализации». Есть несколько уроков, если вы используете Google для «базовой локализации»

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

0 голосов
/ 27 июня 2013
#define PBLocalizedString(key, val) \

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