Локализация WinForms и WPF: resx, спутниковые сборки, переключение культуры
· Обновлено: · Го Комура · CSharp, .NET, WinForms, WPF, Многоязычность, Локализация, Ресурсы, UI, Разработка Windows, Техническая консультация
История изменений (1 обновлений, последнее 30 Aug 2026)
Журнал изменений этой статьи. Там, где версия до правки была заархивирована, она остаётся доступной для чтения по постоянной ссылке с DOI.
- Русский текст переписан как полноценный технический перевод, а не калька с японского. Утверждения статьи не менялись.
- Первая публикация
Цитирование статьи(DOI (зарегистрированный архив): 10.5281/zenodo.21620013)
Приведённые ниже DOI относятся к ранее зарегистрированным архивным версиям, которые могут отличаться от текущего текста. Для ссылки на текущий текст используйте URL этой страницы.
Го Комура (2026). Локализация WinForms и WPF: resx, спутниковые сборки, переключение культуры. KomuraSoft LLC. https://comcomponent.com/ru/blog/winforms-wpf-localization-guide/
- DOI (зарегистрированный архив)
- 10.5281/zenodo.21620013
- DOI (последняя зарегистрированная версия)
- 10.5281/zenodo.21620014
«Хотим то же приложение и в зарубежных офисах», «На заводе появилось больше сотрудников, говорящих на других языках, нужен ещё и английский интерфейс» — как пожелание локализация десктопного приложения Windows звучит просто. Но как только берётесь за работу, сразу цепочка развилок: как пользоваться resx, у WPF несколько способов и непонятно, какой из них правильный, можно ли менять язык на лету.
Статья рассчитана на бизнес-приложения WinForms/WPF. Идём в том порядке, в котором на практике обычно застревают: основа локализации в .NET (культуры и резервный поиск ресурсов), устройство resx и спутниковых сборок, реалистичный выбор способа под каждый фреймворк, переключение языка во время работы и всё, что не строки — форматы, вёрстка, RTL.
1. Сразу к делу (таблица решений)
Способ локализации выбирают по связке фреймворка и требований.
| Ситуация | Рекомендуемый способ | Почему |
|---|---|---|
| WinForms, в основном подписи и кнопки на форме | У формы Localizable = true + resx по языкам (через дизайнер) |
Можно подогнать строки, размер и положение элементов управления под каждый язык; всё делается в Visual Studio |
| Общее для WinForms/WPF: много сообщений и общих формулировок | Общий resx (Resources.resx + Resources.ja.resx и т. д.) + сгенерированный типизированный класс |
К строкам из кода обращаетесь типобезопасно. Одни и те же формулировки делятся между UI и логикой |
| WPF | Общий resx + привязка x:Static |
LocBaml в WPF на .NET (Core) не работает1. Через resx те же приёмы, что в WinForms |
| Нужно переключать язык во время работы | resx + уведомление об изменении (или пересоздание экрана) | Прежде чем выбирать способ, зафиксируйте требование: действительно ли нужно без перезапуска (глава 6) |
| Перевод отдаёте подрядчику | Проектируйте на resx, передавайте таблицу ключ ↔ перевод | resx — XML, его можно отдать как есть, но качество перевода зависит от того, приложите ли вы контекст (какой экран, какая кнопка) |
Выводы заранее.
CurrentCulture(форматы) иCurrentUICulture(язык ресурсов) — разные вещи. На этом различии стоит всё проектирование локализации (глава 2).- По устройству ресурсы — это «нейтральный язык в основной сборке + спутниковые сборки по языкам», а резервный поиск (fallback) идёт сам. Ищут в порядке
ja-JP→ja→ нейтральный, поэтому даже если перевода нет, приложение не падает, а показывает текст языка по умолчанию2. - Для WinForms практическое решение — сочетать подход через дизайнер (
Localizable = true) и общий resx. Экраны — через дизайнер, MessageBox и общие формулировки — в общий resx (глава 4). - LocBaml из официального пошагового руководства по WPF в .NET (Core) не работает1. Сейчас первый кандидат — resx +
x:Static(или привязка) (глава 5). - Для переключения языка во время работы первый кандидат — «применить при следующем запуске». Ресурсы уже открытых экранов сами не подменяются (глава 6).
- Перевод строк — только половина локализации. Вторая половина — вёрстка под длину переведённого текста, форматы дат и чисел, шрифты и языки с письмом справа налево (RTL) (глава 7).
На схеме сплошная линия обозначает отношение, которое выполняется всегда, а пунктирная — условное отношение (условия указаны в пояснении к каждому отношению на странице сведений). Полный список отношений (всего 23, с доказательствами и степенью уверенности) и определения основных понятий собраны на странице сведений карты знаний (на японском). Данные: JSON-LD / Turtle
2. Основа — чем CurrentCulture отличается от CurrentUICulture
В .NET у культуры (региональных параметров) два свойства с разными ролями3.
| Свойство | Что задаёт | Пример |
|---|---|---|
CultureInfo.CurrentCulture |
Формат даты, чисел и валюты | Показывать 2026/07/07 или 7/7/2026 |
CultureInfo.CurrentUICulture |
На каком языке загружать ресурсы | На кнопке «保存» или «Save» |
Их можно задавать независимо. Например, «интерфейс английский, а даты и валюта — в японском формате» (частое требование для бизнес-приложений, которыми в Японии пользуются сотрудники-иностранцы) естественно записывается как CurrentUICulture = en и CurrentCulture = ja-JP.
Оба значения по умолчанию берутся из настроек ОС, поэтому исходная точка — «если ничего не трогать, приложение следует за ОС». Если язык нужно явно переключать для всего приложения, не настраивайте каждый поток по отдельности — подмените значения по умолчанию.
using System.Globalization;
// Подменяем культуру по умолчанию для всех потоков, которые будут созданы дальше
// (выполнять до создания UI-потока, то есть до Application.Run)
CultureInfo.DefaultThreadCurrentUICulture = new CultureInfo("en"); // язык ресурсов
CultureInfo.DefaultThreadCurrentCulture = new CultureInfo("ja-JP"); // формат
Присвоение Thread.CurrentThread.CurrentUICulture действует только на этот поток. В бизнес-приложениях, где async/await и фоновая работа постоянно переходят с потока на поток, надёжнее менять значения по умолчанию целиком — через DefaultThreadCurrentUICulture / DefaultThreadCurrentCulture3.
Форматы логов, файлового вывода и обмена между системами к культуре пользователя не привязывайте: фиксируйте их на CultureInfo.InvariantCulture. Про форматы дат в целом см. «Дата, время и часовые пояса в бизнес-приложениях».
3. Как устроены ресурсы — resx, спутниковые сборки, резервный поиск
Локализация в .NET построена по модели «ступица и спицы» (hub-and-spoke). Ресурсы языка по умолчанию (нейтрального) встраиваются в основную сборку, а ресурсы по языкам выносятся в отдельные DLL — спутниковые сборки (satellite assemblies)4.
После сборки resx по языкам дают примерно такую структуру выходных файлов.
MyApp.exe ← основная сборка (внутри нейтральные ресурсы)
MyApp.dll
ja/MyApp.resources.dll ← спутниковая сборка для японского
en/MyApp.resources.dll ← спутниковая сборка для английского
Во время выполнения ResourceManager, отталкиваясь от CurrentUICulture, ищет ресурсы в порядке конкретная культура (ja-JP) → нейтральная культура (ja) → значение по умолчанию (основная сборка)2. Благодаря этому автоматическому резервному поиску приложение не падает, даже если одного перевода нет: показывается строка языка по умолчанию. И наоборот: если экран «английский», а местами всплывает японский, это признак пропуска в переводе — его можно использовать как критерий тестов (глава 8).
На практике держите в голове два момента.
- Объявите, какая культура является языком по умолчанию, атрибутом
NeutralResourcesLanguage. Тогда при совпадении с культурой по умолчанию не будет лишнего поиска спутниковой сборки, и конечная точка резервного поиска станет однозначной4. - Спутниковые сборки работают только если их раздают вместе с основной сборкой, поэтому в инсталлятор или в схему копирования файлов нужно включать языковые каталоги (глава 8).
Как объявлять NeutralResourcesLanguage, зависит от формата проекта. Сначала старый формат, где AssemblyInfo.cs ведёте сами (так по умолчанию в .NET Framework).
// Properties/AssemblyInfo.cs
using System.Resources;
// Объявляем, что ресурсы, встроенные в основную сборку, — японские
[assembly: NeutralResourcesLanguage("ja-JP")]
В проектах в стиле SDK (по умолчанию начиная с .NET 5) AssemblyInfo.cs генерируется при сборке, поэтому культуру задают свойством в файле проекта.
<!-- MyApp.csproj -->
<PropertyGroup>
<NeutralLanguage>ja-JP</NeutralLanguage>
</PropertyGroup>
В Visual Studio то же самое задаётся в свойствах проекта: [Пакет] > [Общие], параметр «Нейтральный язык сборки». Если в проекте в стиле SDK задать оба варианта, атрибут окажется объявлен дважды и сборка упадёт с CS0579. Оставьте что-то одно.
4. Практика WinForms — дизайнер и общий resx
В WinForms два способа, и на практике их сочетают.
4.1 Экраны — через дизайнер (Localizable = true)
Если у формы поставить Localizable в true, переключить свойство Language на нужный язык (например, английский) и править Text у подписей и кнопок, Visual Studio сама создаст resx этого языка, например Form1.en.resx5. По шагам это выглядит так.
- Откройте форму в дизайнере и выберите саму форму, а не элемент управления (клик по фону).
- В окне Properties (F4) поставьте
LocalizableвTrue. С этого момента строки, размеры и положения элементов управления записываются вForm1.resxязыка по умолчанию. - В том же окне переключите
Languageс «(по умолчанию)» на нужный язык (например, English). - В этом состоянии правьте
Text, размер и положение подписей и кнопок. Изменения сохраняются вForm1.en.resx; resx языка по умолчанию не трогается. - Когда закончили, верните
Languageв «(по умолчанию)».
Если забыть шаг 5 и продолжить обычные правки вёрстки, они попадут только в resx языка, который сейчас выбран, а не в язык по умолчанию. Потом это всплывает как «на японском кнопка стоит не там». В команде, которая работает через дизайнер, о возврате Language договоритесь сразу.
Сильная сторона этого способа: по языкам сохраняются не только строки, но и размер с положением элементов управления. Проблему «登録» → «Register», текст вылез за кнопку, можно снять прямо в дизайнере, подогнав размер под язык. Оборотная сторона — стоимость сопровождения: resx растут как число языков × число форм, и после каждой правки вёрстки формы нужно смотреть все языки. Если языков станет больше, лучше в первую очередь закрыть это вёрсткой (глава 7) и собирать экраны так, чтобы они не разъезжались без ручной подгонки.
4.2 Сообщения и общие формулировки — через общий resx
Строки, которые берутся из кода — тексты MessageBox, диалогов подтверждения, строки состояния — собирайте в общем для проекта Resources.resx и добавляйте по языкам Resources.en.resx и т. п. Из resx генерируется типизированный класс, поэтому из кода к строкам обращаетесь без опечаток в ключе.
// Обращение через сгенерированный типизированный класс.
// Какая строка вернётся, зависит от CurrentUICulture
MessageBox.Show(
Properties.Resources.ConfirmDeleteMessage,
Properties.Resources.ConfirmTitle,
MessageBoxButtons.YesNo);
Самое важное правило здесь: не пишите строки, которые видит пользователь, прямо в коде. Трудозатраты на локализацию уходят не на подключение механизма, а на «сбор раскиданных по коду строк». Именно поэтому имеет смысл вести строки через resx уже тогда, когда приложение ещё на одном языке.
5. Практика WPF — первый кандидат resx, а не LocBaml
Сначала термины, которые встречаются в этой главе.
| Термин | Как читать / официальное имя | Смысл |
|---|---|---|
| BAML | Binary Application Markup Language | Двоичная форма, в которую XAML компилируется при сборке. В WPF-приложении в сборку встраивается и во время выполнения читается не сам XAML, а этот BAML |
| LocBaml | ── | Образец инструмента эпохи .NET Framework: вынимает из BAML строки для перевода, подставляет перевод и собирает BAML заново по языкам1 |
x:Static |
── | Запись, которой XAML читает само значение статического свойства, поля или константы на стороне C#. Чтение одноразовое — в момент построения экрана; если значение потом изменится, экран сам не обновится |
| Спутниковая сборка | ── | DLL вида код-языка/MyApp.resources.dll только с ресурсами этого языка (глава 3) |
Локализация WPF — зона, где источники легко путаются. В официальном walkthrough описан способ: задать UICulture в файле проекта, получить BAML-ресурсы и подставить перевод инструментом LocBaml. Но LocBaml — образец инструмента только для WPF на .NET Framework; в WPF на .NET (Core) он не работает1. Для нового проекта сегодня это не тот способ, который стоит выбирать.
Практический первый кандидат — тот же общий resx, что в WinForms, и обращение к нему из XAML.
<!-- Модификатор доступа resx ставим Public и читаем через x:Static -->
<Window xmlns:res="clr-namespace:MyApp.Properties"
Title="{x:Static res:Resources.MainWindowTitle}">
<Button Content="{x:Static res:Resources.SaveButton}" />
</Window>
Плюс в том, что механизм ресурсов (спутниковые сборки, резервный поиск) полностью общий с WinForms, и даже в смешанном решении тексты можно вести в одном месте. x:Static — статическая ссылка, которая разрешается при построении окна, поэтому если нужно переключать язык во время работы, схему развивают в привязку с уведомлением об изменении (глава 6).
В XAML стоит ещё задать xml:lang у корневого элемента: тогда языкозависимые возможности WPF — расстановка переносов, подстановка шрифтов, отображение цифр — работают как надо1.
Сам выбор между WinForms и WPF разобран в «Таблица решений: WinForms, WPF или WinUI».
6. Переключение языка во время работы — первый кандидат «при следующем запуске»
Если по требованиям язык выбирают в настройках, момент применения берут из двух вариантов.
- Вариант A: применить при следующем запуске (рекомендуется). Выбранный язык сохраняете в файле настроек, а
DefaultThreadCurrentUICultureзадаёте доApplication.Runпри старте (в WPF — до запускаApplication). Реализация простая, и сбои из-за пересборки экранов (потеря того, что пользователь как раз вводит, повторная подписка обработчиков событий и т. п.) просто не из чего возникнуть. - Вариант B: сразу. Даже если сменить
CurrentUICulture, строки на уже открытых экранах сами не подменяются. В WinForms при подходе через дизайнер ресурсы заново накладывают на открытые формы черезComponentResourceManager.ApplyResources; в WPF строки подают через привязку с уведомлением об изменении. В любом случае это доработка в расчёте на то, что «все экраны умеют заново применять ресурсы».
В бизнес-приложениях язык меняют редко (обычно один раз при внедрении), и затраты на реализацию и тесты варианта B часто не окупаются. Рекомендуем брать вариант A по умолчанию, а вариант B — только если это действительно нужно по требованиям. Языковая настройка — на пользователя, поэтому её место — под %LOCALAPPDATA%. Куда класть настройки, см. таблицу решений в «Не только appsettings.json — управление конфигурацией бизнес-приложений Windows».
6.1 Сохранение и чтение языка (реализация варианта A)
Варианту A хватает примерно такого кода. Каталог для файла — свой, под Environment.SpecialFolder.LocalApplicationData (= %LOCALAPPDATA%).
using System;
using System.Globalization;
using System.IO;
internal static class LanguageSetting
{
private static readonly string Dir = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
"MyCompany", "MyApp");
private static readonly string FilePath = Path.Combine(Dir, "language.txt");
// Сохранить язык, выбранный в настройках (например "en", "ja-JP")
public static void Save(string cultureName)
{
Directory.CreateDirectory(Dir);
File.WriteAllText(FilePath, cultureName);
}
// Вызывать при старте, до Application.Run (в WPF — до запуска Application).
// Нет файла или он битый — ничего не делаем = следуем языку интерфейса ОС
public static void ApplyOnStartup()
{
if (!File.Exists(FilePath)) return;
var name = File.ReadAllText(FilePath).Trim();
if (name.Length == 0) return;
try
{
CultureInfo.DefaultThreadCurrentUICulture = new CultureInfo(name);
}
catch (CultureNotFoundException)
{
// Неизвестное имя культуры. Игнорируем настройку, оставляем язык интерфейса ОС
}
}
}
Смысл в том, чтобы отсутствие файла настроек означало «следовать за ОС». При первом запуске или если файл повреждён, приложение не останавливается с исключением, а остаётся на значении CurrentUICulture по умолчанию (глава 2).
6.2 Минимальный каркас варианта B (сразу)
Если по требованиям без немедленного применения никак, ниже — каркас. В обоих случаях условие то же: все экраны сидят на этом механизме.
В WinForms при подходе через дизайнер (Localizable = true) языковой resx заново накладывают на открытую форму через ComponentResourceManager.
using System.ComponentModel;
using System.Globalization;
using System.Windows.Forms;
public partial class MainForm : Form
{
// После смены языка вызывать на каждой открытой форме
public void ApplyCurrentLanguage(CultureInfo culture)
{
CultureInfo.DefaultThreadCurrentUICulture = culture;
CultureInfo.CurrentUICulture = culture; // чтобы подействовало и на текущий UI-поток
var resources = new ComponentResourceManager(GetType());
// Сама форма (заголовок, размер) сохранена под ключом "$this"
resources.ApplyResources(this, "$this");
ApplyRecursive(Controls, resources);
}
private static void ApplyRecursive(
Control.ControlCollection controls, ComponentResourceManager resources)
{
foreach (Control control in controls)
{
resources.ApplyResources(control, control.Name);
ApplyRecursive(control.Controls, resources); // вложенные панели тоже обходим
}
}
}
Эта рекурсия ходит только по Control, поэтому пункты меню и панели инструментов (ToolStripItem — это не Control) нужно отдельно обойти через ToolStrip.Items и сделать то же самое. «Язык везде сменился, кроме меню» — обычно из-за этого.
В WPF x:Static (чтение значения один раз) меняют на привязку и при смене языка кидают уведомление об изменении.
using System.ComponentModel;
using System.Globalization;
using System.Windows.Data;
// Источник строк. Из XAML привязываемся к этому экземпляру
public sealed class Loc : INotifyPropertyChanged
{
public static Loc Instance { get; } = new Loc();
private Loc() { }
// Ключ resx через индексатор. Если строки нет — возвращаем имя ключа как есть
public string this[string key] =>
Properties.Resources.ResourceManager.GetString(key, CultureInfo.CurrentUICulture) ?? key;
public event PropertyChangedEventHandler PropertyChanged;
public void ChangeLanguage(CultureInfo culture)
{
CultureInfo.DefaultThreadCurrentUICulture = culture;
CultureInfo.CurrentUICulture = culture;
// Уведомление об изменении индексатора целиком. Binding.IndexerName = "Item[]"
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(Binding.IndexerName));
}
}
<!-- Модификатор доступа resx — Internal или Public -->
<Window xmlns:loc="clr-namespace:MyApp">
<Button Content="{Binding Path=[SaveButton], Source={x:Static loc:Loc.Instance}}" />
</Window>
От прямой ссылки x:Static из главы 5 отличие одно: это привязка, поэтому она принимает уведомление об изменении. resx и спутниковые сборки под капотом те же, поэтому переход с варианта A на B не требует переделки ресурсов — меняется способ обращения из XAML.
7. Локализация помимо строк — вёрстка, форматы, шрифты, RTL
Даже когда перевод готов, локализация сделана только наполовину.
- Растяжение и сжатие вёрстки. При том же смысле длина текста по языкам сильно меняется (японское «保存» → немецкое «Speichern»). Вёрстка с фиксированными координатами и размерами разъезжается после каждого перевода, поэтому в WinForms официально рекомендуют сочетать
TableLayoutPanelиAutoSize, чтобы вёрстка следовала за длиной строки6. В WPF система вёрстки и так гибкая: в большинстве случаев достаточно не фиксировать ширину жёстко. Имеет смысл проектировать это вместе с защитой от разъезда на высоком DPI («Высокий DPI в WinForms», «Высокий DPI в WPF») — тогда не придётся переделывать. - Формат даты, чисел и валюты. Отображение на экране отдайте
CurrentCultureи уберите из кода экранов зашитыеToString("yyyy/MM/dd"). Наоборот, файлы, логи и данные интеграции фиксируйте наInvariantCulture(глава 2). - Шрифты. Проверьте, есть ли в шрифте глифы нужного языка. Если добавляете китайский (упрощённый/традиционный) или корейский, шрифт, подобранный под японский, может дать неестественные начертания (проблема глифов CJK). Если UI-шрифт задан в одном месте, добавить язык потом проще.
- Языки с письмом справа налево (RTL). Для арабского и иврита мало перевести строки — нужно ещё зеркалировать экран по горизонтали. В WinForms этим управляют
RightToLeft/RightToLeftLayout, в WPF —FlowDirection. Если RTL «добавлять потом», придётся пересматривать все экраны, поэтому если такой язык может понадобиться, закладывайте его в требования сразу. - Текст внутри картинок и значков. Строки, впечатанные в изображение, механизм ресурсов не переключает. Надёжнее не класть текст в картинку, а собирать «изображение + текстовая подпись».
Общие решения по UI см. также в «Таблица решений по UX приложений Windows».
8. Эксплуатация — передача перевода, дырки, распространение
И наконец — как этим жить, когда механизм уже есть.
- К переводу прикладывайте контекст. Если отдать только ключи resx и исходный текст, короткие формулировки вроде «OK» или «開く» переводятся нестабильно. Рабочая схема: обмениваться таблицей (электронная таблица и т. п.), где к ключу, оригиналу и переводу добавлено «на каком экране и зачем эта строка», а после получения перевода заносить его в resx.
- Пропуски в переводе ловите через резервный поиск. Благодаря резервному поиску пропуск проявляется как «на экране этого языка всплывает язык по умолчанию»2. Простая проверка в CI (скрипт или тест), которая сверяет ключи языковых resx с нейтральным, отлавливает это автоматически до релиза.
- Кладите спутниковые сборки в дистрибутив. Если не раздавать языковые каталоги вроде
ja/иen/целиком, для этого языка сработает резервный поиск, и получится «перевели, а на экране нет». Пропуск языкового каталога при ручном копировании — классическая ошибка, поэтому структуру каталогов включайте в инсталлятор (MSI/MSIX и т. д.) целиком. При распространении через ClickOnce спутниковые сборки всех языков по умолчанию попадают в развёртывание7. Выбор способа распространения в целом — в «Как выбрать способ распространения приложения Windows».
Итог
Локализация десктопного приложения стоит на давно устоявшихся resx и спутниковых сборках; сам механизм несложный. На практике решает другое: проектирование с разведением CurrentCulture и CurrentUICulture, дисциплина не писать пользовательские строки прямо в коде, вёрстка, которая выдерживает длину перевода, и трезвая оценка, нужно ли переключение сразу. Для WPF не цепляйтесь за устаревшие материалы про LocBaml — сейчас практическое решение собирать всё на resx.
Выбор способа локализации уже существующего приложения и оценка затрагиваемого кода, а также экраны, отчёты и данные интеграции под зарубежные офисы часто требуют решения с опорой на текущую кодовую базу. Если не уверены — напишите нам.
Связанные статьи
- Таблица решений: WinForms, WPF или WinUI
- Таблица решений по UX приложений Windows
- Дата, время и часовые пояса в бизнес-приложениях
- Высокий DPI в WinForms
- Высокий DPI в WPF
- appsettings.json — ещё не всё: управление конфигурацией бизнес-приложений Windows
- Как выбрать способ распространения приложения Windows (MSI / MSIX / ClickOnce / xcopy / свой updater)
- Как устроен профиль пользователя Windows
Смежные направления консультаций
В 合同会社小村ソフト (Komura Soft) консультируем по проектированию и реализации многоязычного интерфейса приложений WinForms/WPF, а также по выбору способа и оценке затрагиваемого кода, когда локализуют уже существующее приложение.
Источники
-
Microsoft Learn, WPF Globalization and Localization Overview. О генерации спутниковых сборок через
UICultureв файле проекта, о ролиxml:langу корневого элемента и о том, что LocBaml — образец инструмента только для WPF на .NET Framework и в WPF на .NET не работает. ↩ ↩2 ↩3 ↩4 ↩5 -
Microsoft Learn, Retrieve resources in .NET apps. О резервном поиске ресурсов (конкретная культура → нейтральная культура → ресурсы по умолчанию в основной сборке). ↩ ↩2 ↩3
-
Microsoft Learn, CultureInfo.CurrentUICulture Property. О том, что
CurrentUICultureиспользуется ResourceManager при поиске ресурсов конкретной культуры, что значение по умолчанию берётся из языка интерфейса ОС, и о задании значения по умолчанию для всех потоков черезDefaultThreadCurrentUICulture. ↩ ↩2 -
Microsoft Learn, Resources in .NET apps. О создании и упаковке ресурсов resx, о модели «ступица и спицы» с основной и спутниковыми сборками и об атрибуте
NeutralResourcesLanguage. ↩ ↩2 -
Microsoft Learn, Walkthrough: Localizing a Hybrid Application. О том, как в конструкторе Windows Forms поставить
Localizableвtrue, переключитьLanguageи получить resx языка (например Form1.es-ES.resx), и о примере установкиCurrentUICultureдо вызоваInitializeComponent. ↩ -
Microsoft Learn, How to: Design a Windows Forms Layout that Responds Well to Localization. О том, как через
TableLayoutPanelсобрать вёрстку, в которой размер элементов управления следует за длинойTextпосле локализации. ↩ -
Microsoft Learn, Localize ClickOnce applications. О том, что включение всех спутниковых сборок в одно развёртывание — поведение Visual Studio по умолчанию, и что во время выполнения берётся спутниковая сборка под культуру ОС. ↩
Похожие статьи
Недавние статьи с теми же тегами помогут подробнее изучить близкие темы.
Печать и PDF в Windows-приложениях: PrintDocument, WPF и библиотеки отчётов
Печать WinForms через PrintDocument, печать WPF через FlowDocument и FixedDocument и варианты вывода PDF сведены в таблицу по требованиям...
Окончание драйверов принтера Windows ── как готовить печать форм и этикеток в бизнес-приложениях
Microsoft поэтапно прекращает сопровождение драйверов принтера v3/v4; с июля 2026 IPP class driver предпочтут. Что исчезает в Windows pro...
CI/CD для WinForms / WPF: сборка, подпись и распространение в GitHub Actions
Практическое руководство по CI/CD для WinForms / WPF в GitHub Actions. Минимальный YAML сборки и тестов на windows-latest, нумерация верс...
Значок в области уведомлений и toast в Windows-приложении: ошибки NotifyIcon и выбор AppNotification
Разбираем, как держать бизнес-приложение Windows в области уведомлений и показывать toast. Правильная работа с NotifyIcon, повторная реги...
Не создавайте HttpClient в using на каждый запрос — создание, тайм-ауты и повторные попытки в C#
Если создавать HttpClient через using на каждый запрос, исчерпываются сокеты; static-экземпляр не отслеживает смену DNS. Разбираем схемы ...
Связанные темы
Эти страницы показывают тему статьи в более широком контексте услуг и решений.
Технические темы Windows
Раздел о разработке Windows, расследовании сбоев и использовании существующих активов.
Поток UI и таймеры
Поток UI WPF / WinForms, асинхронные операции, Dispatcher и проектирование таймеров.
Услуги по этой теме
Статья напрямую связана со следующими услугами.
Разработка приложений для Windows
Проектирование и реализация многоязычного интерфейса десктопного приложения — практическая область разработки под Windows.
Технические консультации и ревью дизайна
Когда локализуют уже готовое приложение, выбор способа и оценка затрагиваемого кода обычно требуют разбора проектных решений — это техническая консультация.
Частые вопросы
Вопросы, которые часто возникают при консультациях по теме статьи.
- Можно ли локализовать уже существующее приложение, которое изначально писали только на японском?
- Можно, но основная работа — не «подключить механизм ресурсов», а «найти и заменить строки, прописанные прямо в коде». Нужно пройтись не только по текстам на экранах, но и по MessageBox, логам, текстам исключений, отчётам и строкам для отображения, которые лежат в БД. Объём определяется не числом экранов, а тем, насколько строки размазаны по коду. Если уже на этапе новой разработки хотя бы пользовательские строки идут через resx, потом локализация обойдётся на порядок дешевле.
- Можно ли обойтись машинным переводом?
- Для внутреннего инструмента или временного решения начать с машинного перевода — нормально. Но в бизнес-приложениях много коротких слов на кнопках и в меню (например, «登録», «取消»), и без контекста машинный перевод в этой зоне ошибается чаще. Минимум один проход ревью с человеком, который реально работает на этом языке, стоит заложить. Важнее качества перевода — заранее через TableLayoutPanel и AutoSize убедиться, что более длинные строки после перевода не развалят вёрстку.
- Нужно ли переводить логи и сообщения об ошибках?
- Практический стандарт: сообщения на экране пользователя переводить, а то, что пишется в лог-файлы и журнал событий, не переводить (зафиксировать на одном языке). Логи читает не пользователь, а разработчик или эксплуатация; многоязычные логи ломают поиск при разборе инцидентов. Форматы (даты, числа) в логах тоже фиксируйте на CultureInfo.InvariantCulture. Если CurrentCulture и CurrentUICulture изначально разведены, такое разделение получается само собой.
- Стоит ли давать переключение языка в самом приложении отдельно от языка интерфейса ОС?
- Для зарубежных офисов или для бизнес-приложений, которыми в Японии пользуются сотрудники-иностранцы, своя языковая настройка в приложении очень полезна. Спокойная схема из двух уровней: по умолчанию следовать языку интерфейса ОС (значение CurrentUICulture по умолчанию) и переопределять его только если пользователь явно выбрал язык в настройках. Самый безопасный вариант — применять переключение «при следующем запуске»: и реализация проще, и поведение проще объяснить.
Об авторе
Страница с профилем автора статьи.
Го Комура
Представитель KomuraSoft LLC
Специализируется на разработке программного обеспечения для Windows, техническом консалтинге и расследовании сбоев, особенно в проектах с унаследованными системами и трудно воспроизводимыми ошибками.