Ефективні стратегії локалізації в .NET [закрито]


121

Я розробляю інтерфейс для програми .NET MVC, яка потребуватиме міжнародної локалізації всього контенту найближчим часом. Я дуже добре знайомий з .NET в цілому, але ніколи не мав проекту, який потребував би такого значного фокусу на міжнародній доступності.

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


2
Чудове запитання! Я зіткнувся з подібною ситуацією і хотів би бачити, як експерти зважуються на це.

Хтось отримав якісь хороші стандарти управління ресурсами? Локалізоване значення також може містити зображення, а не лише рядки.

1
Це інтерфейс WPF / Silverlight або Winforms? З мого (обмеженого) досвіду досвід WinForms щодо локалізації набагато простіше, ніж WPF / Silverlight.
Піт Стеншнес

1
Якщо ви в кінцевому підсумку зберігати ваші локалізовані рядки в базі даних, а файли ресурсів, ви можете захотіти поглянути на цю дискусію: stackoverflow.com/questions/2458615 / ...

1
@ Pete, @smartcaveman сказав, що "розробляє інтерфейс користувача для .NET MVC-програми", тому ...
BrunoSalvino

Відповіді:


74

Ви розробляєте додаток ASP.Net MVC, чи не так? Інші відповіді здаються специфічними для настільних програм. Дозвольте мені зробити звичайні речі:

Виявлення локалів

Досить важливо, щоб ваша програма правильно виявляла локальну інформацію користувача. У настільних додатках CultureInfo.CurrentCulture має кращу локалізацію форматування (ту, яку слід використовувати для форматування чисел, дат, валют тощо), тоді як CultureInfo.CurrentUICulture має кращий локальний інтерфейс користувача (той, який слід використовувати для відображення локалізованих повідомлень) . Для веб-додатків слід встановити обидві культури для автоматичного (для автоматичного виявлення локалі з заголовка AcceptLanguage), якщо ви не хочете реалізувати певний робочий процес виявлення локалів (тобто хочете підтримати зміну мови на вимогу).

Екстерналізація рядків

Усі рядки повинні надходити з ресурсів, тобто файлів Resx. У програмі Winforms це легко досягти, встановивши для властивості форми Localizable значення true. Вам також знадобиться вручну (на жаль) екстерналізувати рядки, що надходять з ваших моделей. Це також відносно просто. У Asp.Net вам знадобиться зовнішнє все вручну ...

Макети

Вам обов'язково потрібно дозволити розширення рядків. У світі Winforms це можна досягти за допомогою TableLayoutPanel, який слід використовувати, щоб переконатися, що макет автоматично налаштується для розміщення більш тривалого тексту. У веб-світі вам трохи не пощастило. Можливо, вам потрібно буде застосувати механізм локалізації CSS - спосіб змінити (замінити) визначення CSS. Це дозволить людям з локалізації змінювати проблеми стилю на вимогу. Переконайтеся, що кожен елемент HTML на наданій сторінці має унікальний ідентифікатор - це дозволить точно націлити його.

Проблеми культури

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

ToString () та Аналіз ()

Обов’язково завжди передайте CultureInfo під час виклику ToString (), якщо він не підтримується. Таким чином ви коментуєте свої наміри. Наприклад: якщо ви використовуєте якесь число внутрішньо і чомусь потрібно перетворити його на рядкове використання:

int i = 42;
var s = i.ToString(CultureInfo.InvariantCulture);

Для номерів, які відображатимуться користувачеві:

var s = i.ToString(CultureInfo.CurrentCulture); // formatting culture used

Те саме стосується Parse (), TryParse () і навіть ParseExact () - деякі неприємні помилки можуть бути введені без належного використання CultureInfo. Це тому, що якась бідна душа в Microsoft, сповнена добрих намірів, вирішила, що це гарна ідея вважати CultureInfo.CurrentCulture як замовчуванням (вона буде використана, якщо ви нічого не передаєте) - зрештою, коли хтось використовує ToString ( ) він / вона хоче відобразити це користувачеві, правда? Виявляється, це не завжди так, наприклад, спробуйте зберегти номер версії додатка в базі даних, а потім перетворити його в екземпляр класу Version. Удачі.

Дати та часові пояси

Не забудьте завжди зберігати та інстанціювати DateTime у UTC (використовуйте DateTime.UtcNow замість DateTime.Now). Перетворіть його на місцевий час у локальному форматі після відображення:

DateTime now = DateTime.UtcNow;
var s = now.ToLocalTime().ToString(CultureInfo.CurrentCulture);

Якщо вам потрібно надіслати електронні листи з посиланням на час у тілі, обов’язково введіть інформацію про часовий пояс - включіть як зміщення UTC, так і список міст:

DateTime someDate; // i.e. from database
var formattedDate = String.Format("{0} {1}", 
             someDate.ToLocaleTime().ToString(CultureInfo.CurrentCulture),
             TimeZoneInfo.Local.DisplayName);

Складені повідомлення

Вас уже попередили не поєднувати рядки. Замість цього ви, ймовірно, використовуєте String.Format (), як показано вище. Однак я мушу зазначити, що ви повинні мінімізувати використання складних повідомлень. Це просто тому, що правила цільової граматики зазвичай відрізняються, тому перекладачам може знадобитися не лише перевпорядкувати речення (це вирішиться за допомогою заповнювачів та String.Format ()), але й перекласти все речення по-різному на основі що буде замінено. Дозвольте навести кілька прикладів:

// Multiple plural forms
English: 4 viruses found.
Polish: Znaleziono 4 wirusy. **OR** Znaleziono 5 wirusów.

// Conjugation
English: Program encountered incorrect character | Application encountered incorrect character.
Polish: Program napotkał nieznaną literę | Aplikacja napotkała nieznaną literę.

Інші проблеми з об'єднанням

Конкатенація не обмежується рядками. Уникайте складання елементів керування разом, скажімо:

Нагадати ще раз у [текстовому полі з номером] днів.

Це слід переробити на щось на кшталт: Нагадуйте мені знову за цю кількість днів: [текстове поле].

Кодування символів та шрифти

Завжди зберігайте, передавайте будь-який текст у Unicode (тобто в UTF-8). Не робіть шрифти з жорстким кодом - Можливо, локалізація може змінити їх, і він вимкне механізм зворотного зворотного шрифту (за умови Winforms). Не забудьте дозволити "дивні" символи в більшості полів (тобто ім'я користувача).

Тест

Ймовірно, вам потрібно буде реалізувати так званий псевдопереклад, тобто створити ресурси для німецької культури і скопіювати англійські рядки, додавши префікс і суфікс. Ви також можете обернути заповнювачі, щоб легко виявити складені рядки. Мета псевдоперекладу - виявити проблеми локалізації, такі як жорстко закодовані рядки, проблеми з компонуванням та надмірне використання складних повідомлень.


5
Щодо складених повідомлень - мені довелося одноразово робити множинні форми. Я простягнув String.Formatтак , щоб він міг підтримувати цей прохолодний синтаксис: "There {0:was|were} {0} {0:virus|viruses} found."Кожна мова може завантажити свої власні правила, так що ви могли б зробити "Znaleziono {0} {0:wirusy|wirusów}." джерело на GitHub: github.com/scottrippey/SmartFormat/wiki
Скотт Риппи

2
@Scott Rippey Ви помітили, що в польському прикладі написано "Znaleziono 4 wirusy. АБО Znaleziono 5 wirusów". <- Польська, як і багато інших мов, має більше двох форм множини, і правила їх розрізнення можуть бути також складними. Тут я повинен залишити польську мову, оскільки я не розмовляю нею, але моєю мовою форма множини для 101 речі така сама, як і для 1 речі. Ви можете поглянути, як GNU gettext вирішує цю проблему: gnu.org/s/hello/manual/gettext/Plural-forms.html
gregopet

2
@gregopet Мій польський приклад був надуманий, тому що я не говорю про це, але саме це робить проект SmartFormat. Ось найкращий приклад: "{0} {0:plik|pliki|plików}". Форматор має польське правило, яке визначає, яку з 3 форм використовувати, і правильно визначає спеціальні випадки. Зараз я працюю над тим, щоб додати більше правил, тому gettextстаття виявиться дуже корисною, дякую.
Скотт Ріппей

Для псевдо локалізації я створив безкоштовний онлайн-інструмент для псевдолокалізації на
pseudolocalize.com

74

Деякі основні речі, які ви повинні врахувати:

Екстерналізація всіх рядкових ресурсів

Усі ваші ресурси повинні міститися у зовнішніх файлах, які можна передавати для локалізації. Не забувайте про повідомлення про помилки, якщо ви хочете, щоб вони також були локалізованими.

Дозвольте достатньо місця для розширення рядків

Рядки на деяких мовах, як правило, до 30% довші (наприклад, грецькою), наприклад, тому переконайтеся, що ви розробили інтерфейс користувача таким чином, щоб рядки могли розширюватися при необхідності. Ось досить крайній приклад для французької мови:

Гаразд -> Accepter (французька - 400% розширення)

Я б рекомендував зробити якийсь псевдопереклад як вихідну точку ( http://en.wikipedia.org/wiki/Pseudolocalization ). Або ви можете перекласти свої ресурси через Google Translate або Bing. Це дасть вам хорошу вказівку на те, як виглядатимуть фактичні переклади.

Слідкуйте за текстом у зображеннях

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

Ніколи не жорстко кодуйте будь-які шляхи до папок Windows

Очевидно, але я це бачив і раніше. Наприклад, C:\Program Filesперекладається на деяких міжнародних версіях Windows, наприклад, C:\Programmeна німецькій ОС.

Уникайте використання конкретних термінів для місцевості

Наприклад, якщо ви запитаєте когось про його "середню школу" на бланку, це в Західній Європі мало значення.

Уникайте створення рядків за допомогою конкатенації рядків

Наприклад, це виглядає нешкідливо:

strWelcome = ReadExternalString("Welcome"); 
strMessage = strWelcome + ", " + UserName;

Але, наприклад, порядок слів на японській мові був би іншим, тому це може закінчитися, не маючи сенсу.

Налаштування часу / дати

Завжди переконайтеся, що ви отримуєте формат часу / дати в ОС.


@ Jimmy C, як ти йдеш про створення рядків для незалежної від мови логічної послідовності?
smartcaveman

14
@Smart зробіть щось на вашому ресурсі, наприклад "{0}, {1}", тоді, коли ви локалізуєте його, використовуйте string.format та введіть привітання та ім'я користувача. Плюс це дає вам користь від того, що "Поточна {0} швидкість - {1} {2}", і ви можете перейти в "Двигун", "50" і "MPH", і коли ви перекладете своє речення, ви можете перемістити { 0} тощо навколо того, де вони мають сенс у цій мові
taylonr

4
Хороший список JimmyC. "Ніколи не жорстко кодуйте будь-які шляхи до папок Windows" нагадав мені "Завжди використовувати Path.Combine" замість конкатенації рядків для шляхів до Windows.

@ Jimmy-C Відмінна відповідь!

1
Environment.GetFolderPath можна використовувати для отримання дійсних шляхів до загальних шляхів, таких як "Мої документи", не залежно від англійської назви цих папок.
Crippledsmurf

24

Спеціальні міркування щодо азіатських мов

Окрім усіх чудових відповідей, які вже є тут, слідкуйте за азіатськими мовами:

Остерігайтеся різної довжини тексту

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

Однак текст японської мови, як правило, значно довший, навіть довший, ніж еквівалентний англійський текст з точки зору кількості символів.

Слідкуйте за базовою схемою та виглядом "ковзаючи вгору"

Азіатські символи, як правило, викладаються на базовій лінії , які не включають низхідні елементи (тобто нижню частину y, g, q, j тощо), коли ви форматуєте елемент екрана - зазвичай кнопки - з текстом всередині, і якщо це текст є лише азіатськими мовами (тобто немає західних алфавітів), тоді текст буде виглядати так, як він зміщений вгору.

Форматування чисел та локалізованих числових одиниць

Обробляйте форматування номерів по-різному. У різних азіатських країнах є різні способи форматування чисел. Те саме з валютами. Наприклад, у Східній Азії 10 000 (ван) є загальною одиницею. В Індії поширене 100 000 (лак).

Місцеві валюти

Валюти деяких країн мають багато нулів і не мають десяткових знаків (наприклад, Японія, Індонезія, Італія), а інші мають до двох цифр після коми.

Остерігайтеся різних порядків слів

Порядок слів може не завжди бути однаковим. Найкраще використовувати {0}, {1} тощо у форматуванні рядків замість жорсткого кодування порядку слів, якщо ваш рядок походить із комбінації різних фрагментів даних.

Використовуйте специфічний для місцевості сорт

Сортування відрізняється за мовою та за локальною ознакою - завжди слід покладатися на певний тип локалізації O / S.

Будьте дуже обережні з символами повної ширини / півширини

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

Період не є крапкою ... кома - це не кома ...

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

Номери телефонів

Не припускайте нічого у форматуванні номера телефону. Не завжди існує код міста тощо, і його можна відформатувати по-різному. Зазвичай мають рядок формату для кожної країни.

Не припускайте, що люди матимуть лише один номер мобільного телефону або один номер факсу тощо. В Азії це не так.

Адреси - щільніші, ніж ви можете подумати

Щодо адрес, не припускайте нічого . Не завжди може бути поштовий індекс. Поштові індекси не завжди можуть бути цифрами. Країна може не мати провінцій / штатів. Країна може бути просто великим містом (наприклад, Сінгапур). Для деяких азіатських країн найменшою одиницею будинку може бути "Кімната X, блок Y, секція Z, поверх A, блок B, група C, садиба D". Загалом, будьте дуже ліберальні за кількістю полів та кількістю символів, дозволених у адресах.

Вітання

Вітання не обмежуються лише містером, місіс і т. Д. Хоча ви, мабуть, безпечні у використанні "М" та "F" для сексу - ми ще не такі вже й ...


1
Останній абзац змусив мене посміхнутися.
BoltClock

О, ми (хлопці з i18n) ще навіть не почали ... Нам вдалося лише подряпати поверхню :) Якби ми говорили про конкретні проблеми, такі як підтримка GB18030, наш пост був би занадто довгим, щоб ТАК впорався :) Дякую для вашої записки все одно я пропустив досить багато предметів.
Paweł Dyda

Щодо останнього, я вважаю, Великобританія зараз офіційно сприймає "Інше" як секс. Подумайте трансгендерів.
Барт Фрідеріхс

11

Деякі основні кроки - переконатися, що будь-яка рядок, який відображається на екрані, не є літералом у вашому коді. Якщо ви робите Winforms, кожна форма матиме ресурс інтерфейсу користувача. Для діалогів, звітів тощо обов’язково використовуйте файли ресурсів проекту.

Тож замість "Не вдалося завантажити" у вашому коді, у вас може виникнути щось на кшталт Resources.UploadFailed

Таким чином ви можете створити новий файл ресурсів для кожної мови, якою ви користуєтесь (і .Net допоможе в цьому.) І мати локалізований рядок у кожному файлі.

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


1
З мого досвіду L10n, російська мова є найгіршим сценарієм. Однак у Winforms з належним користувачем TableLayoutPanel можна витончено впоратися зі зростанням рядків.
Paweł Dyda

Так, мій досвід був обмежений 7 мовами: англійською, німецькою, португальською, італійською, французькою, іспанською та японською мовами. Але я можу бачити, що російська мова погана, оскільки вони, як правило, мають багато суфіксів та префіксів
taylonr

9

Я пропоную запустити аналіз FXCop або Visual Studio Code (вони зовсім однакові) на своїх зборах.

Вони добре виявляють .NET-код, який не використовує належних перевантажень, орієнтованих на культуру, як цей: CA1305: Вкажіть IFormatProvider .

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


Це за замовчуванням чи мені потрібно вказати якесь налаштування для пошуку правил, що стосуються глобалізації?
smartcaveman

@smartcaveman - це за замовчуванням (хм .. насправді, деякі люди думають, що в цих інструментах є багато правил за замовчуванням :-)
Simon Mourier

7

На додаток до конкретного способу завантаження ресурсів я б переконався, що для початку ви протестуєте з псевдо локалізованою версією. Інакше ви, ймовірно, не помітите місця, де міркування щодо інтернаціоналізації були пропущені до кінця.


Для швидкого і простий спосіб pseudolocalize, я побудував безкоштовний онлайн інструмент в pseudolocalize.com
JerSchneid

6

Окрім усіх інших корисних підказок, тут відсутні деякі з них:

Враховуйте, що деякі країни використовують більше однієї мови. Наприклад, у Канаді користувач очікує, що зможе легко переключатися між англійською та французькою мовами.

Якщо ви задаєте користувачеві питання, на яке очікується відповідь в одній літері, не сподівайтеся, що користувач натисне клавішу "Y", щоб сказати "Так".

Будьте в курсі збережених програм, що дати в SQL БД відповідають формату США

Розміщення текстових рядків у БД дозволяє пізніше додавати додаткові мови без повторної розстановки.

Під час надсилання письмових текстових файлів для перекладу завжди додайте опис контексту, щоб переконатись, що перекладач вибрав правильне слово. Наприклад, без контексту, ви можете перевести "крок:" у щось, що стосується звуку або місця, де ви граєте у футбол

Мітки адреси завжди потребують перетворення. Провінція в Канаді, штат в Америці, графство у Великобританії


5

Вам потрібно врахувати:

  1. Маршрут на багатомовність

  2. Перемістіть увесь рядок жорсткого коду до файлу ресурсу

Приклад властивості:

Модель:

[Display(Name = <Resource for display name>.<field for this property>)]
[Required(ErrorMessage = <Resource for error message>.<field for this validate message>)]
public string TestProperty { get; set; }

Вид:

@Html.LabelFor(m=>m.TestProperty)
@Html.EditorFor(m => m.TestProperty)
@Html.ValidationMessageFor(m => m.TestProperty)

5

Ось щось не згадується в решті відповідей.

Залежно від складності вашої програми та її локалізації, я б дуже радив впровадити альтернативний постачальник ресурсів та зберігати локалізовані ресурси в базі даних. За схемою локалізації ASP.NET за замовчуванням всі ресурси зберігаються у файлах RESX, які:

  1. Чи болить поперек для редагування у Visual Studio
  2. Обмежте розповсюдження та управління локалізованими ресурсами після складання / відвантаження / запуску програми.

В якості можливого випадку використання розгляньте надання мовних пакетів для вашої програми та можливість імпорту та експорту мов через інтерфейс користувача. Тут не допоможуть файли RESX.

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


1
Дякуємо, що знайшли час для перегляду цих чудових відповідей і все ще сприяєте чомусь новому та корисному.
smartcaveman

+1; Я створив велику веб-програму в Asp.NET, і ми закінчили робити переклади через базу даних. Нові функції часто додавались, але, оскільки наші перекладачі не були експертами в конкретній термінології, яку ми використовували, ми змогли швидко звернутись до роздратованих електронних листів клієнтів типу "Чому ви використовуєте слово Y для X, що явно не так?".
gregopet

3

Найголовніше - це керування вмістом різними мовами. Я сам розробив пару веб-програм, і управління вмістом різною мовою є найбільшою проблемою.

Я використовую Базу даних для зберігання ресурсів / контенту. Це дає мені гнучкість додавання будь-якої мовної підтримки, яку я хочу. Я реалізував логіку повернення до англійської мови, якщо ресурс певної мови не знайдений.

Пізніше ви можете використовувати перекладач, щоб перетворити значення англійської мови на будь-яку мову.


2

Короткий зміст речей, які слід враховувати при інтернаціоналізації:

  • Вся інформація повинна бути інтернаціоналізована. Врахуйте, що в графіці може бути інформація, яку ми хочемо інтернаціоналізувати.

  • Розмір полів або рядків, залежно від мови, оскільки це може спричинити нам проблеми.

  • Порядок слів залежить від мови, якою ми є, тому порядок в одній мові буде однаковим в іншій.

  • Ми повинні врахувати, що формат дати зміниться з однієї мови на іншу


1

Зробіть тест на Туреччину :

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

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

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

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