Для начала — небольшой ликбез.
Microsoft предлагает следующие способы для локализации WPF приложений: локализация с помощью утилиты locbaml, с помощью resx файлов и с помощью xml.
Локализация с использованием resx файлов является стандартным .net подходом и является продуманным и проверенным решением. Тем не менее, Microsoft рекомендует именно locbaml подход, который и использовался в нашем приложении. Однако, мы решили отказаться от него, из-за чрезмерной сложности при локализации и переключиться на традиционные resx ресурсы.
С первой проблемой — конвертацией строчек из ResourceDictionary в resx файл справиться легко, благо и то и другое — xml, а вот с использованием resx ресурсов в WPF приложении дело обстоит сложней. В интернете я так и не нашёл достаточно простого способа использовать строчки из resx файлов в xaml разметке. Всё что предлагалось — раличные варианты расширения разметки т.е. для доступа к ресурсам использовать конструкции вида: Text="{x:Static res:Resources.LabelText}. Что абсолютно неприемлимо, так как в приложении таких мест было тысячи. Однако, я всё таки нашёл приемлимое решение.
Итак, для примера создадим пустое WPF приложение. Откроем его ресусры (Properties -> Resources.resx) и добавим строчку с именем LabelText и содержимым:

Далее скопируем этот файл и назовём его Resources.ru-Ru.resx и изменим строчку в ресурсах:

Затем, изменим App.xaml:
Microsoft предлагает следующие способы для локализации WPF приложений: локализация с помощью утилиты locbaml, с помощью resx файлов и с помощью xml.
Локализация с использованием resx файлов является стандартным .net подходом и является продуманным и проверенным решением. Тем не менее, Microsoft рекомендует именно locbaml подход, который и использовался в нашем приложении. Однако, мы решили отказаться от него, из-за чрезмерной сложности при локализации и переключиться на традиционные resx ресурсы.
С первой проблемой — конвертацией строчек из ResourceDictionary в resx файл справиться легко, благо и то и другое — xml, а вот с использованием resx ресурсов в WPF приложении дело обстоит сложней. В интернете я так и не нашёл достаточно простого способа использовать строчки из resx файлов в xaml разметке. Всё что предлагалось — раличные варианты расширения разметки т.е. для доступа к ресурсам использовать конструкции вида: Text="{x:Static res:Resources.LabelText}. Что абсолютно неприемлимо, так как в приложении таких мест было тысячи. Однако, я всё таки нашёл приемлимое решение.
Итак, для примера создадим пустое WPF приложение. Откроем его ресусры (Properties -> Resources.resx) и добавим строчку с именем LabelText и содержимым:

Далее скопируем этот файл и назовём его Resources.ru-Ru.resx и изменим строчку в ресурсах:

Затем, изменим App.xaml:
Copy Source | Copy HTML
- <Application x:Class=«WpfApplication1.App»
- xmlns=«schemas.microsoft.com/winfx/2006/xaml/presentation»
- xmlns:x=«schemas.microsoft.com/winfx/2006/xaml»
- Startup=«Application_Startup»
- StartupUri=«MainWindow.xaml»>
- <Application.Resources>
- </Application.Resources>
В обработчик событий пропишем:
Copy Source | Copy HTML
- private void Application_Startup(object sender, StartupEventArgs e)
- {
- ResourcesLoader.LoadResources(this.Resources);
- }
Теперь самое интересное — класс ResourceLoader. Добавим его в проект, класс будет содержать два метода:
Copy Source | Copy HTML
- static internal class ResourcesLoader
- {
- // Получаем словарь ресурсов для сборки с заданной культурой.
- static private ResourceDictionary _GetResourceDictionary(CultureInfo cultureInfo)
- {
- ResourceDictionary dictionary = new ResourceDictionary();
- var resourceSet = Properties.Resources.ResourceManager.GetResourceSet(cultureInfo, true, true);
- foreach (DictionaryEntry item in resourceSet)
- dictionary.Add(item.Key, item.Value);
-
- return dictionary;
- }
-
- static public void LoadResources(ResourceDictionary resourceDictionary)
- {
- // Получаем словарь для основйной сборки.
- ResourceDictionary defaultDictionary = _GetResourceDictionary(DEFAULT_CULTURE);
-
- // Слдиваем этот словарь с остальными словарями приложения
- resourceDictionary.MergedDictionaries.Add(defaultDictionary);
-
- // Если текущая культура отличается от культуры по умолчанию -
- // грузим ресурсы из саттелитной сборки текущей культуры
- if (Thread.CurrentThread.CurrentUICulture.CompareInfo !=
- DEFAULT_CULTURE.CompareInfo)
- {
- ResourceDictionary localizedDictionary =
- _GetResourceDictionary(Thread.CurrentThread.CurrentUICulture);
- resourceDictionary.MergedDictionaries.Add(localizedDictionary);
- }
- }
-
- private static CultureInfo DEFAULT_CULTURE = CultureInfo.GetCultureInfo("en-US");
- }
Всё, теперь ресурсы без проблем можно использовать из xaml'a:
Copy Source | Copy HTML
- <Window x:Class="WpfApplication1.MainWindow"
- xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
- xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- Title="MainWindow" Height="100" Width="225">
- <Label Content="{DynamicResource LabelText}" FontSize="36" />
- </Window>
-
Теперь при запуске приложения в русской системе будет выводиться:

В системе с английской локалью:

Если нужно перевести на другой язык — создаём соответствующий resx файл и переводим.
Насчёт производительности. Я тестировал на файле из 1200 строк, всё слияние словарей занимает 20 миллисекунд, т.е. никакой ощутимой задержки при загрузке это не вызовет. Единственная проблема, которая остаётся — текст не отображается во время разработки в дизайнере студии.
Ссылки:
Статья на хабре, где подробно описан способ с использованием Markup-Extension.
Проект. VS 2010.