Найкраща практика використання NSLocalizedString


140

Я (як і всі інші) використовую 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);

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

Тож, хоча це працює дещо добре, мені було цікаво, як ви, хлопці, робите це у своїх проектах. Чи існують інші підходи для спрощення використання NSLocalizedString? Чи, можливо, існує навіть рамка, яка його інкапсулює?


Я просто роблю це майже так само, як і ти. Але я використовую макрос NSLocalizedStringWithDefaultValue для створення різних файлів рядків для різних проблем локалізації (наприклад, контролерів, моделей тощо) та для створення початкового значення за замовчуванням.
анка

Схоже, що Експорт до локалізації xcode6 не вловлює рядки, визначені як макроси у файлі заголовка. Чи може хто-небудь підтвердити чи сказати мені, що я можу пропустити? Дякую...!
Суддстер

@Juddster, може підтвердити, навіть з новим редактором фонду-> Експорт для локалізації він не потрапляє до заголовкового файлу
Червоний

Відповіді:


100

NSLocalizedStringмає кілька обмежень, але він є настільки центральним для какао, що писати нестандартний код для локалізації нераціонально, тобто вам доведеться його використовувати. Однак, трохи інструментів може допомогти. Ось як я продовжую:

Оновлення файлу рядків

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

Використання однієї і тієї ж струни в різних місцях

Якщо ви використовуєте один і той же рядок кілька разів, ви можете або використовувати макрос, як і раніше, або кешувати його як змінну примірника у контролері подання даних або у вашому джерелі даних. Таким чином, вам не доведеться повторювати опис, який може застаріти і стати непослідовним серед екземплярів тієї самої локалізації, що завжди заплутано. Оскільки змінні екземпляри є символами, ви зможете використовувати автоматичне завершення цих найбільш поширених перекладів, а також використовувати "ручні" рядки для конкретних, які в будь-якому разі трапляться лише один раз.

Я сподіваюся, що ви будете більш продуктивні з локалізацією какао за допомогою цих порад!


Дякую за вашу відповідь, я обов'язково погляну на ваш файл python. Я погоджуюся з вашими умовами іменування. Я нещодавно спілкувався з іншими розробниками iOS, і вони рекомендували використовувати статичні рядки замість макросів, що має сенс. Я підтримав вашу відповідь, але зачекаю трохи, перш ніж я прийму її, тому що рішення все ще трохи незграбне. Можливо, прийде щось краще. Знову дякую!
JiaYow

Вас дуже вітають Локалізація - це втомливий процес, наявність правильних інструментів та робочого процесу робить світ різницею.
ndfred

17
Я ніколи не розумів, чому функції локалізації у стилі gettext використовують один із перекладів як ключ. Що станеться, якщо ваш оригінальний текст зміниться? Ваш ключ змінюється, і всі локалізовані файли використовують старий текст для свого ключа. Мені це ніколи не було сенсу. Я завжди використовував ключі типу "home_button_text", тому вони унікальні і ніколи не змінюються. Я також написав сценарій bash для розбору всіх моїх файлів Localizable.strings та генерування файлу класу статичними методами, які завантажують відповідний рядок. Це дає мені заповнити код. Одного разу я можу це відкрити.
Майк Веллер

2
Я думаю, ти genstringsне маєш на увазі gestring.
hiroshi

1
@ndfred час компіляції перевіряє, що ви неправильно набрали рядок - це найбільший виграш. Це все-таки незначно більше коду. Також у випадку рефакторингу, статичний аналіз, наявність символу, полегшить ситуацію.
Аллен Дзенг

31

Що стосується автокомплектування рядків у Xcode, ви можете спробувати https://github.com/questbeat/Lin .


3
Це насправді досить дивовижно. Не потрібно створювати макроси.
Beau Nouvelle

1
сторінку не знайдено_
Juanmi

1
@Juanmi Дякую за те, що згадуєте про мертве посилання. Я замінив посилання на URL-адресу github.
hiroshi

24

Погодьтеся з ndfred, але я хотів би додати це:

Другий параметр можна використовувати як ... значення за замовчуванням !!

(NSLocalizedStringWithDefaultValue не працює належним чином із генстрингом, тому я запропонував це рішення)

Ось моя спеціальна реалізація, яка використовує 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. Використовуйте!

Переконайтеся, що ви додали сценарій запуску у фази збірки додатків, щоб файл Localizable.strings оновлювався при кожній збірці, тобто нова файлова локалізована лінія буде додана у файл Localized.strings:

Мій сценарій фази збірки - це сценарій оболонки:

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à :-)


Отримання помилок ARC. Невідомий метод екземпляра для вибору "localizedString: comment: :(
Mangesh

Я думаю, це тому, що LocalizationHandlerUtil.h відсутній. Я не можу знайти код ... Просто спробуйте створити файл заголовка LocalizationHandlerUtil.h, і це повинно бути добре
Pascal

Я створив файли. Я думаю, це пов'язано з проблемою шляху папки.
Мангеш

3

У Swift я використовую наступне, наприклад для кнопки "Так" у цьому випадку:

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

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

Якщо value:параметр не використовувався, перший параметр буде використовуватися як для ідентифікатора перекладу, так і для текстового значення за замовчуванням. Файл Localizable.strings містив би "Yes" = "Yes";. Цей вид управління файлами локалізації здається дивним. Особливо, якщо перекладений текст довгий, тоді ідентифікатор довгий. Щоразу, коли змінюється будь-який символ текстового значення за замовчуванням, змінюється ідентифікатор перекладу. Це призводить до проблем при використанні зовнішніх систем перекладу. Зміна ідентифікатора перекладу розуміється як додавання нового тексту перекладу, який може бути не завжди бажаним.


2

Я написав сценарій для підтримки Localizable.strings на декількох мовах. Хоча це не допомагає в автоматичному завершенні, воно допомагає об'єднати .strings файли за допомогою команди:

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

Для отримання додаткової інформації див: https://github.com/hiroshi/merge_strings

Дехто з вас вважає це корисним, сподіваюся.


2

Якщо хтось шукає рішення Swift. Ви можете перевірити моє рішення, яке я зібрав тут: SwiftyLocalization

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

Коротше кажучи, кроки: Електронна таблиця Google -> 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 для перетворення таблиць -> CSV та скрипту Python для перетворення файлів CSV -> Localizable.strings Ви можете швидко переглянути цей приклад, щоб дізнатися, що можливо.


1

з iOS 7 та Xcode 5 вам слід уникати використання методу "Localization.strings" та використовувати новий метод "базової локалізації". Якщо у Google ви перейдете на "базову локалізацію", можна ознайомитись із деякими навчальними посібниками.

Документ Apple: Базова локалізація


так, Стів, це правильно. Також вам все ще потрібен метод файлу .strings для будь-якого динамічно створеного рядка. Але лише для них кращим методом Apple є локалізація бази.
Ронні Веберс

Посилання на новий метод?
Гіпербола

1
Отже, базовий метод локалізації марний. Ви все ще повинні зберігати інші файли локації для динамічних рядків, і вони зберігають ваші рядки, розповсюджені через безліч файлів. Рядки всередині Nibs / Сценографічних дощок
Rafael Nobre


0

Сам я часто захоплююся кодуванням, забуваючи помістити записи у файли .strings. Таким чином, у мене є помічники скриптів, щоб знайти те, що я зобов'язаний повернути у .strings файли та перекласти.

Оскільки я використовую власний макрос над NSLocalizedString, будь ласка, перегляньте та оновіть скрипт перед тим, як використовувати, оскільки для простоти я припустив, що нуль використовується як другий параметр для 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"

Звичайно, можна відполірувати більше, але я подумав.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.