# General Translation Knowledge Base: Как перевести файлы .resx в приложении .NET?
URL: https://generaltranslation.com/ru/kb/translate-resx-files.mdx
Docs index: https://generaltranslation.com/llms.txt
Description: Создайте копию каждого нейтрального файла .resx с кодом культуры в имени, например Resources.fr.resx, и переводите только значение каждой записи, не меняя имена, комментарии и заполнители вроде {0}. .NET компилирует каждую культуру в сателлитную сборку, а при отсутствии перевода переходит от fr-CA к fr и далее к нейтральному файлу. CLI General Translation создаёт эти файлы культур на основе нейтрального источника.
Category: Перевод
Last updated: 2026-10-28
Authors: General Translation Team
Published: 2026-10-28

Файл .resx — это XML-список именованных ресурсов. Нейтральный файл, например `Resources.resx`, содержит строки на языке по умолчанию, а каждый перевод хранится в соседнем файле с кодом культуры в имени, например `Resources.fr.resx`: в нём те же имена, но с переведёнными значениями. Во время выполнения `ResourceManager` выбирает файл, соответствующий текущей культуре UI, а недостающие ресурсы ищет по цепочке родительских культур.

## Что содержит файл .resx

Каждый ресурс представлен элементом `<data>`. Его атрибут `name` — это ключ, по которому ваш код находит ресурс, `<value>` содержит сам текст, а необязательный `<comment>` подсказывает переводчику, для чего этот текст нужен. Вот фрагмент (в реальном файле в начале также есть блок схемы и записи `<resheader>`, которые Visual Studio добавляет автоматически):

```xml title="Resources/SharedResource.resx"
<data name="Checkout" xml:space="preserve">
  <value>Checkout</value>
  <comment>Button that starts payment for the items in the cart.</comment>
</data>
<data name="ItemsInCart" xml:space="preserve">
  <value>You have {0} items in your cart.</value>
  <comment>{0} is the number of items.</comment>
</data>
```

Во французском файле те же атрибуты `name`, но с французскими значениями. Меняются только строковые значения. Имена служат ключами для поиска, а нестроковые ресурсы, например изображения, у которых есть атрибут `type` и зачастую `mimetype`, обычно остаются без изменений. Microsoft рекомендует редактировать файлы .resx в Visual Studio, а не вручную, поскольку XML должен оставаться корректно сформированным и соответствовать предопределённому формату ([Microsoft: Create resource files](https://learn.microsoft.com/en-us/dotnet/core/extensions/create-resource-files)).

## Как .NET находит нужный перевод

Давайте каждому файлу перевода то же корневое имя, что и у нейтрального файла, и указывайте культуру перед расширением: `Resources.fr.resx`, `Resources.fr-CA.resx`. При сборке нейтральные ресурсы встраиваются в основную сборку, а ресурсы каждой культуры компилируются в сателлитную сборку, которая размещается в папке с именем культуры, например `fr/MyApp.resources.dll`.

Когда приложение запрашивает строку, .NET сначала ищет её в сателлитной сборке для запрошенной культуры, затем — для родительской культуры и так далее вверх по цепочке, а если ничего не найдено, использует нейтральные ресурсы. Для запроса `fr-CA` порядок такой: `fr-CA`, затем `fr`, затем нейтральный файл. Если файла ресурсов нет даже для культуры по умолчанию, выбрасывается исключение `MissingManifestResourceException` или `MissingSatelliteAssemblyException`. В Linux и macOS имя папки культуры должно в точности совпадать с `CultureInfo.Name`, включая регистр ([Microsoft: Package and deploy resources](https://learn.microsoft.com/en-us/dotnet/core/extensions/package-and-deploy-resources)).

Благодаря резервному механизму достаточно перевести контент один раз для целой группы регионов. Microsoft рекомендует завести один файл `de` для пользователей из `de-DE`, `de-AT` и `de-CH`, а в региональных файлах хранить только строки, которые отличаются. Обратная сторона: отсутствующий перевод никогда не приводит к исключению — пользователь просто видит нейтральный язык, а сборка никак об этом не сообщает. (См. [Что такое коды локалей?](/kb/locale-codes).)

## Использование .resx в ASP.NET Core, Windows Forms и WPF

### ASP.NET Core

Зарегистрируйте службы локализации, выберите поддерживаемые культуры и подключите промежуточное ПО ([Microsoft: Select language and culture](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/localization/select-language-culture)):

```csharp title="Program.cs"
builder.Services.AddLocalization(options => options.ResourcesPath = "Resources");

builder.Services.Configure<RequestLocalizationOptions>(options =>
{
    var supportedCultures = new[] { "en-US", "fr" };
    options.SetDefaultCulture(supportedCultures[0])
        .AddSupportedCultures(supportedCultures)
        .AddSupportedUICultures(supportedCultures);
});

var app = builder.Build();

// Определяет культуру по строке запроса, cookie-файлу или заголовку Accept-Language — именно в этом порядке.
app.UseRequestLocalization();
```

Затем внедрите `IStringLocalizer<T>` и получайте строки по имени, передавая после него аргументы форматирования: `localizer["ItemsInCart", count]`. Файлы ресурсов именуются по полному имени типа класса без имени сборки, поэтому `Controllers.HomeController` читает `Controllers.HomeController.fr.resx` из папки `Resources` (или `HomeController.fr.resx` из `Resources/Controllers`), а пустой класс-маркер `SharedResource` объединяет строки, общие для всего приложения. `SupportedUICultures` определяет, какие файлы .resx будут прочитаны, а `SupportedCultures` — форматирование дат и чисел ([Microsoft: Provide localized resources](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/localization/provide-resources)).

Если перевода нет, `IStringLocalizer` возвращает сам ключ, поэтому Microsoft описывает рабочий процесс вообще без нейтрального .resx: `localizer["About Title"]` выводит «About Title» на английском ([Microsoft: Make content localizable](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/localization/make-content-localizable)). Для отображения этого достаточно, но в таком случае нет исходного файла, который мог бы прочитать переводчик или инструмент. Если вы хотите генерировать переводы из файла, используйте именованные ключи и нейтральный .resx, значениями в котором служит английский текст, — это традиционный подход, также описанный Microsoft.

### Windows Forms и WPF

В Windows Forms задайте для свойства формы **Localizable** значение `true`, а затем выберите культуру в свойстве **Language**. После этого при изменении текста в конструкторе Visual Studio записывает его в файл культуры, например `Form1.es-ES.resx` ([Microsoft: Localizing a hybrid application](https://learn.microsoft.com/en-us/dotnet/desktop/wpf/advanced/walkthrough-localizing-a-hybrid-application)). Файлы, созданные конструктором, содержат и нетекстовые записи, например имена, начинающиеся с `>>`. Приложения WPF могут загружать строки из .resx через `ResourceManager`. В пошаговом руководстве Microsoft по локализации XAML используется инструмент LocBaml, который сама Microsoft называет примером, «не готовым к использованию в рабочей среде» ([Microsoft: Localize a WPF app](https://learn.microsoft.com/en-us/dotnet/desktop/wpf/advanced/how-to-localize-an-application)).

### Редактирование в Visual Studio

Начиная с Visual Studio 2022 версии 17.11, Resource Explorer показывает каждый файл .resx вместе со всеми его культурами в одной таблице. Щёлкните файл .resx правой кнопкой мыши и выберите **Open With &gt; Resource Explorer** (или нажмите **Ctrl+Alt+R**). Инструмент предупреждает об отсутствующих переводах («No value») и о заполнителях, которые не совпадают с нейтральной культурой, например `Hello, {name}!` и `Hola, {nombre}!` ([Microsoft: Manage application resources](https://learn.microsoft.com/en-us/visualstudio/ide/managing-application-resources-dotnet)).

## Что ломается при переводе файлов .resx

* **Заполнители.** Элементы составного форматирования в .NET имеют вид `{index[,width][:formatString]}`, например `{0}`, `{1,-10}` или `{0:N2}`. В переводе их можно переставлять или использовать один и тот же элемент дважды, но элемент форматирования, ссылающийся на несуществующий аргумент, выбрасывает исключение `FormatException` во время выполнения, а пропущенный элемент приводит к незаметной потере значения. Фигурные скобки как обычные символы записываются в виде `{{` и `}}` ([Microsoft: Composite formatting](https://learn.microsoft.com/en-us/dotnet/standard/base-types/composite-formatting)).
* **Имена.** Если атрибут `name` переведён, поиск по ключу ничего не находит, и для строки используется нейтральный язык.
* **XML.** Значение, содержащее `&` или `<`, должно оставаться экранированным (`&amp;` и `&lt;`), иначе файл не скомпилируется.
* **Клавиши доступа.** В Windows Forms `&File` подчёркивает букву F. В переводе должен быть ровно один символ `&` перед подходящей буквой.
* **Комментарии.** Комментарии — это контекст для переводчиков, а не текст для перевода. В руководстве Microsoft по Windows App SDK отмечается, что переводить их не нужно ([Microsoft: Localize strings](https://learn.microsoft.com/en-us/windows/apps/windows-app-sdk/mrtcore/localize-strings)).
* **Рассинхронизация.** Когда разработчик добавляет имя в нейтральный файл, оно должно появиться и во всех файлах культур. Резервный механизм маскирует такие пробелы, поэтому перед каждым релизом проверяйте, не пропущены ли значения.

## Способы перевода файлов .resx

| Подход                                              | Сохраняет имена и нестроковые ресурсы                                                     | Проверка заполнителей                                                                                                  | Синхронизация культур                                                                                                                       | Когда подходит                                                    |
| --------------------------------------------------- | ----------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------- |
| Visual Studio Resource Explorer, вручную            | Да                                                                                        | Предупреждает о несоответствиях                                                                                         | Показывает отсутствующие значения для каждой культуры                                                                                       | Один-два языка, которыми владеет кто-то в команде                 |
| Переводчики или система управления переводами (TMS) | Да, если TMS умеет разбирать .resx                                                        | Зависит от её проверок качества                                                                                         | Через синхронизацию TMS с репозиторием                                                                                                      | Переводом занимаются профессиональные переводчики или подрядчик   |
| Скрипт на основе API машинного перевода или LLM     | Только если скрипт разбирает XML и отправляет на перевод только значения                  | Пишете сами                                                                                                             | Повторный запуск и сравнение изменений                                                                                                      | Вы готовы поддерживать скрипт и его валидацию                     |
| Вставить файл в чат с ИИ-ассистентом                | Ничего не гарантировано                                                                   | Нет                                                                                                                     | Вручную                                                                                                                                     | Разовый файл, который вы проверите построчно                      |
| General Translation CLI                             | Переводит только строковые значения; имена, комментарии и нестроковые ресурсы сохраняются | Перевод, в котором заполнитель изменён или потерян, отбрасывается, и для этой записи .NET использует резервное значение | Повторно использует переводы неизменённых записей; запускайте [`gt translate`](/docs/cli/reference/commands/translate) перед каждым релизом | Файлы ресурсов хранятся в репозитории и меняются с каждым релизом |

Resource Explorer подойдёт для одного-двух языков, перевод на которые может проверить коллега. [TMS](/kb/what-is-a-translation-management-system) стоит выбрать, если профессиональные переводчики работают вне кодовой базы. General Translation подходит, когда файлы .resx меняются с каждым релизом, а переведённые файлы нужно коммитить вместе с кодом. (См. [Сравнение General Translation и TMS](/kb/general-translation-vs-tms).)

## Как переводить файлы .resx с помощью General Translation

CLI от General Translation, `gt`, переводит файлы .resx, а также файлы `.resw` с той же схемой, с помощью файлового ключа `resx` (`gt` версии 2.23.1 или новее). В имени нейтрального файла культура не указывается, поэтому укажите его в `include`, исключите соседние переведённые файлы и с помощью `transform` добавьте культуру перед расширением:

```json title="gt.config.json"
{
  "defaultLocale": "en",
  "locales": ["fr", "es-MX"],
  "files": {
    "resx": {
      "include": ["Resources/*.resx"],
      "exclude": ["Resources/*.[locales].resx"],
      "transform": {
        "match": "\\.resx$",
        "replace": ".{locale}.resx"
      }
    }
  }
}
```

В папке `Resources` эта конфигурация читает `Controllers.HomeController.resx` и записывает `Controllers.HomeController.fr.resx` и `Controllers.HomeController.es-MX.resx`, сохраняя принятые в ASP.NET Core имена с точками. Перечислите в `locales` все культуры, которые уже поставляются с приложением: исключение `[locales]` пропускает переведённые файлы только для настроенных локалей, поэтому `Resources.de.resx`, не попавший в список, будет прочитан как исходный файл. Для проектов UWP и WinUI, где файлы `.resw` хранятся в отдельной папке для каждого языка, используйте `"include": ["Strings/[locale]/Resources.resw"]` без `transform`.

CLI работает на Node.js 20 или новее, даже в репозитории .NET. Установите его, как показано в разделе [Быстрый старт CLI](/docs/cli/quickstart), войдите с помощью [`npx gt login`](/docs/cli/reference/commands/login) и задайте `GT_PROJECT_ID` ([учётные данные CLI](/docs/cli/guides/configuring#credentials)). Если в приложении уже есть переводы, сначала импортируйте их с помощью [`npx gt upload`](/docs/cli/reference/commands/upload). Затем выполните [`npx gt translate`](/docs/cli/reference/commands/translate). В CI сохраните в `GT_API_KEY` ключ API проекта с правами **Files &gt; Write** и **Translation queue &gt; Enabled**.

Переведённый файл — это копия исходного с заменёнными строковыми значениями, поэтому имена, порядок записей, схема и блоки `<resheader>`, комментарии и нестроковые ресурсы остаются без изменений. Записи с `mimetype`, с типом, отличным от `System.String`, с именем дизайнера `>>` или с пустым значением на перевод не отправляются. Каждый `<comment>` передаётся как контекст, а инструкции Microsoft по локализации в комментариях, например `{Locked}`, соблюдаются. Заполнители вроде `{0}`, `{0:N2}`, `{1,-10}`, `{count}` и удвоенные фигурные скобки сохраняются, одиночная клавиша доступа `&` остаётся перед подходящей буквой, а у загруженного файла сохраняются кодировка и метка порядка байтов исходного файла. (См. [справочник по .resx](/docs/cli/reference/formats/resx-files).)

Проверяйте переводы в [панели управления](/docs/platform/dashboard/guides/reviewing-translations) или в Visual Studio, а после локального редактирования файлов выполняйте [`npx gt save-local`](/docs/cli/reference/commands/save-local), чтобы последующие запуски не затирали ваши правки. [Контекстная группа](/docs/platform/dashboard/guides/defining-context-for-translations) применяет к ресурсам .NET те же Glossary и пользовательские промпты, что и к [JSON-файлам](/kb/translate-json-files) вашего веб-приложения, поэтому название продукта переводится одинаково и там, и там.

## Часто задаваемые вопросы

### Чем .resx отличается от .resw?

Схема у них одна и та же. В приложениях UWP и WinUI файлы `.resw` хранятся в отдельной папке для каждого языка, например `Strings/en-US/Resources.resw` и `Strings/de-DE/Resources.resw`, а не содержат культуру в имени файла ([Microsoft: Localize strings](https://learn.microsoft.com/en-us/windows/apps/windows-app-sdk/mrtcore/localize-strings)).

### Нужен ли отдельный файл для fr-CA, если уже есть fr?

Нет. Если в запросе для `fr-CA` какое-либо имя отсутствует, используется значение из `fr`, поэтому файл `fr-CA` достаточно добавить только для отличающихся строк.

### Почему после добавления французского файла моё приложение ASP.NET Core по-прежнему отображается на английском?

Убедитесь, что `fr` указан в `SupportedUICultures`, что имя файла совпадает с именем типа класса, а в Linux или macOS — что папка культуры в результате сборки (например, `fr`) в точности совпадает с `CultureInfo.Name` с учётом регистра.

## Основные выводы

* Каждый перевод — это отдельный файл .resx рядом с исходным, в имени которого указана культура, например `Resources.fr.resx`; переводите значения, но ни в коем случае не атрибуты `name`.
* .NET переходит от `fr-CA` к `fr`, а затем к нейтральным ресурсам, поэтому при отсутствии перевода вместо ошибки отображается текст на нейтральном языке.
* Если элемент форматирования ссылается на аргумент, которого нет среди переданных, выбрасывается исключение `FormatException`; проверяйте заполнители вида `{0}` в каждом переводе.
* Resource Explorer в Visual Studio 2022 17.11 помечает отсутствующие значения и несовпадающие заполнители во всех культурах.
* CLI General Translation переводит файлы .resx и .resw с помощью ключа `resx` (версия 2.23.1+): имена, комментарии и нестроковые ресурсы сохраняются, а переводы с нарушенными заполнителями отбрасываются.

## Related questions

- [Как перевести JSON-файлы i18n?](https://generaltranslation.com/ru/kb/translate-json-files.mdx): Переводите только строковые значения в JSON-файле локали. Ключи, не связанные с множественным числом, заполнители вроде {{name}} и теги оставляйте без изменений, но учтите, что ключи для форм множественного числа зависят от языка: например, для польского нужны inbox_few и inbox_many. Используйте инструмент или API с поддержкой формата, а затем разберите и проверьте результат перед вычиткой.
- [Как перевести файлы PO и POT?](https://generaltranslation.com/ru/kb/translate-po-files.mdx): Заполните каждый msgstr, включая все формы множественного числа, которых требует заголовок Plural-Forms, а msgid, msgctxt и заполнители оставьте без изменений. Запустите msgfmt --check, чтобы проверить заголовки, количество форм множественного числа и заполнители в записях с флагом формата, а затем отдельно убедитесь, что не осталось пустых msgstr. CLI General Translation создаёт переведённый PO-файл для каждого языка на основе вашего POT-шаблона.
- [Что такое система управления переводами (TMS)?](https://generaltranslation.com/ru/kb/what-is-a-translation-management-system.mdx): Система управления переводами (TMS) — это программное обеспечение, которое хранит исходное содержимое и его переводы и обеспечивает весь процесс перевода: память переводов, глоссарии, проверку, машинный перевод и интеграции. Память переводов позволяет повторно использовать ранее выполненные переводы при точном или нечётком совпадении. Типичные примеры — Crowdin, Lokalise, Phrase, Smartling и Transifex.
- [General Translation или TMS (Crowdin, Lokalise, Phrase)](https://generaltranslation.com/ru/kb/general-translation-vs-tms.mdx): Выбирайте General Translation, если разработчикам нужны генерация переводов, контекст из кода и проверка, привязанные к релизам продукта. Сохраните существующие каталоги и библиотеку рендеринга или пишите встроенный JSX без ключей перевода. TMS подходит для программ, построенных вокруг языковых поставщиков, CAT-редакторов и обмена XLIFF; её интеграции могут автоматизировать передачу файлов.
- [Как поддерживать переводы в актуальном состоянии при каждом развёртывании?](https://generaltranslation.com/ru/kb/translations-in-ci.mdx): Запускайте перевод как этап конвейера перед каждой сборкой для рабочей среды — тогда новые тексты попадут в то же развёртывание, что и сами изменения, уже переведёнными. В CLI gt от General Translation для этого служит команда npx gt translate, которая использует ключ API проекта и идентификатор проекта, сохранённые как секреты CI.

## Previous and next questions

- Previous question: [Как перевести приложение на Flask или FastAPI?](https://generaltranslation.com/ru/kb/translate-python-app.mdx)
- Next question: [Как перевести файлы PO и POT?](https://generaltranslation.com/ru/kb/translate-po-files.mdx)

## Sitemap

See the [Knowledge Base sitemap](https://generaltranslation.com/ru/kb/sitemap.md) for every article.
