Локализация текста, который включает подстрочные и встроенные изображения в WPF - PullRequest
4 голосов
/ 02 ноября 2009

У меня есть небольшое приложение WPF, которое я работаю над локализацией. Я прочитал несколько документов, но не нашел хорошего источника информации о работе с «богатым» контентом - например:

У меня есть элемент пользовательского интерфейса (TextBlock), который содержит смесь текста с форматированием, встроенными изображениями и символами. Я хочу локализовать этот элемент. Я полагаю, что для локализации этого элемента характерно для другого языка то, что положение формул / символов необходимо будет изменить относительно окружающего текста, и что наилучшее форматирование для текста может немного отличаться в других языках, так как хорошо.

Я ищу предложения, ресурсы и / или подходы для локализации «обогащенного» контента (контента, который смешивает текст, элементы форматирования и встроенные элементы интерфейса) в XAML / WPF. Учитывая упор на композицию и «богатый» пользовательский интерфейс в WPF, я был удивлен, что не нашел никакой информации о сценарии выше - что мне не хватает?

Я думал о том, чтобы сохранить XAML в файле ресурсов и затем проанализировать его во время выполнения для включения в пользовательский интерфейс, а также о создании представления / пользовательского элемента управления, который выгружается в зависимости от локали - но я не вижу упоминаний об этих подходах что заставляет меня задуматься, нахожусь ли я на «неправильном пути») и надеюсь, что у кого-то есть опыт или информация, которой можно поделиться?

Спасибо!

Ответы [ 3 ]

2 голосов
/ 30 декабря 2009

Расширение StaticResource отлично работает для того, что вы пытаетесь достичь. С помощью StaticResource вы можете включить практически все, даже если DynamicResource не работает:

<Window ...>
  <Window.Resources>

    <Span x:Key="Whatever">
      <Bold>Hello</Bold> there<LineBreak/>
      A green circle:
      <InlineUIContainer>
        <Ellipse Width="10" Height="10" Fill="Green" />
      </InlineUIContainer>
    </Inline>

  </Window.Resources>

  ...
  <TextBlock>
    <StaticResource ResourceKey="Whatever" />
  </TextBlock>

Теперь локализовать это легко:

  1. Переместите ресурс Span в отдельный ResourceDictionary и объедините его со словарем ресурсов вашего приложения.
  2. Во время запуска приложения и перед созданием каких-либо окон используйте код для получения текущей культуры и добавьте дополнительный язык ResourceDictionary к application.Resources.MergedDictionaries. Спутниковый словарь можно загрузить с помощью встроенного в WPF механизма локализации.

Пока словари объединяются в словаре приложения в правильном порядке, любой именованный ресурс, найденный в локализованном словаре, будет иметь приоритет над ресурсом в главном словаре, например, ваша dll для испанской локализации может иметь xaml файл, содержащий это:

<ResourceDictionary>
  <Span x:Key="Whatever">
    El círculo rojo
      <InlineUIContainer>
        <Ellipse Width="10" Height="10" Fill="Red" />
      </InlineUIContainer>
    dice <Bold>hola!</Bold>
  </Span>
</ResourceDictionary>

Обратите внимание, что сообщение похоже, но для испанского кружок красный, а расположение текста другое.

Вы можете сделать это намного дальше с ControlTemplates, если хотите. Использование ControlTemplates позволит вам делать такие вещи, как кнопки, расположенные в различном порядке в зависимости от локали. Например, если ваш общий словарь содержит:

<ResourceDictionary>
  <ControlTempate x:Key="Something" TargetType="ContentControl">
    <StackPanel>
      <TextBlock Text="In English we want the text above the button" />
      <ContentPresenter />
    </StackPanel>
  </ControlTemplate>
</ResourceDictionary>

Вы можете добавить это в свое окно или пользовательский элемент управления:

<ContentControl Template="{StaticResource Something}">
  <Button Command="Save">Save File</Button>
</ContentControl>

А затем вы меняете раскладку на другой язык, например:

<ResourceDictionary>
  <ControlTempate x:Key="Something" TargetType="ContentControl">
    <DockPanel>
      <ContentPresenter DockPanel.Dock="Left" />
      <TextBlock Text="En español en el botón a la izquierda del texto" />
        <!-- In spanish the button is to the left of the text -->
    </DockPanel>
  </ControlTemplate>
</ResourceDictionary>

Примечание. Если вы используете локализацию только в DependencyProperties (например, без InlineCollections и т. Д.), Вы можете обойтись без использования {DynamicResource}, которое позволяет изменить локаль в любое время с мгновенным обновлением пользовательского интерфейса. Чтобы сделать это с моим первым примером, вместо включения <Span> в ResourceDictionary и включения его в TextBlock, вы можете поместить TextBlock в ControlTemplate внутри ResourceDictionary.

Это только начало гибкости локализации с WPF. Вы можете пойти гораздо дальше.

0 голосов
/ 30 декабря 2009

Я отметил ответ Рэй Бернса, используя расширение StaticResource в качестве принятого ответа, я думаю, что многие детали об этом подходе идеальны. Я также хотел показать еще одну идею - использовать шаблоны T4 для создания «свободных» xaml-файлов, которые включаются в вывод и анализируются во время выполнения на основе CurrentCulture.

Код ниже является содержимым файла base_block.tt. Одна важная деталь ниже - это использование encoding = "Unicode" (я предполагал, что Utf-8 будет работать, но XamlParser выдает ошибку для некоторых символов, когда шаблон указывает Utf-8, по-видимому, из-за настройки BOM).

<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ output extension=".xaml" encoding="Unicode"#>

<UserControl
    xml:lang="<#= this.xml_lang #>"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <UserControl.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="basic_styles.xaml" />
                <ResourceDictionary Source="equations.xaml" />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </UserControl.Resources>

        <WrapPanel Style="{StaticResource description_wrap_panel_style}">

                <TextBlock  x:Name="c_textblock"
                                Style="{StaticResource description_textblock_style}" 
                                AutomationProperties.Name = "<#= this.textblock_automation_name #>">
                        <#= this.textblock_constant_C_contents #>
                </TextBlock>

                <TextBlock  Style="{StaticResource description_textblock_style}"
                                KeyboardNavigation.TabIndex="1">
                        <#= this.hyperlink_textblock_contents #>
                </TextBlock>

                <TextBox    Style="{StaticResource entry_textbox_style}"
                                AutomationProperties.LabeledBy="{Binding ElementName=c_textblock}"
                                KeyboardNavigation.TabIndex="0">
                </TextBox>

        </WrapPanel>

</UserControl>

<#+ 

private string xml_lang = @"";
private string textblock_constant_C_contents = @"";
private string textblock_automation_name = @"";
private string hyperlink_textblock_contents = @"";

#>

base_block.tt может использоваться во включении в файл .tt, который задает необходимые значения - для английского вот мой файл en.tt, который сгенерирует en.xaml:

<# 

xml_lang = @"en-US";

textblock_constant_C_contents = 
    @"Enter a constant, <Italic>C</Italic>, that satisfies
            <InlineUIContainer Style='{StaticResource image_container_style}'>
                    <Image x:Name='formula_11' Source='{StaticResource equation_11}' Style='{StaticResource image_style}' Tag='3.0'>
                    <Image.Height>
                        <MultiBinding Converter='{StaticResource image_size}'>
                            <Binding Mode='OneWay' ElementName='formula_11' Path='Tag'/>                        
                            <Binding Mode='OneWay' ElementName='c_textblock' Path='FontSize'/>
                        </MultiBinding>
                    </Image.Height>
                </Image>
            </InlineUIContainer>";

textblock_automation_name = @"Enter a Constant, C, that satisfies the following equation: the standard error of the estimate is equal to the constant C over the square root of the sample size";

hyperlink_textblock_contents = @"(<Hyperlink AutomationProperties.Name='More information about the constant C' 
                                        x:Name='c_hyperlink'>more info</Hyperlink>)";

#>

<#@ include file="base_block.tt" #>

Или для французского - fr.tt -> fr.xaml:

<# 

xml_lang = @"fr";

textblock_constant_C_contents = 
    @"Entrez une constante, <Italic>C</Italic>, pour satisfaire
            <InlineUIContainer Style='{StaticResource image_container_style}'>
                    <Image x:Name='formula_11' Source='{StaticResource equation_11}' Style='{StaticResource image_style}' Tag='3.0'>
                    <Image.Height>
                        <MultiBinding Converter='{StaticResource image_size}'>
                            <Binding Mode='OneWay' ElementName='formula_11' Path='Tag'/>                        
                            <Binding Mode='OneWay' ElementName='c_textblock' Path='FontSize'/>
                        </MultiBinding>
                    </Image.Height>
                </Image>
            </InlineUIContainer>";

textblock_automation_name = @"Entrez une constante, C, qui satisfait l'équation suivante: l'erreur-type de l'estimation est égale à la constante C sur la racine carrée de la taille de l'échantillon.";

hyperlink_textblock_contents = @"(<Hyperlink AutomationProperties.Name=""Plus d'informations sur la constante C"">en savoir plus</Hyperlink>)";

#>

<#@ include file="base_block.tt" #>

Приведенный выше французский генерирует следующий файл .xaml:

<UserControl
    xml:lang="fr"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <UserControl.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="basic_styles.xaml" />
                <ResourceDictionary Source="equations.xaml" />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </UserControl.Resources>

        <WrapPanel Style="{StaticResource description_wrap_panel_style}">

                <TextBlock  x:Name="c_textblock"
                            Style="{StaticResource description_textblock_style}" 
                            AutomationProperties.Name = "Entrez une constante, C, qui satisfait l'équation suivante: l'erreur-type de l'estimation est égale à la constante C sur la racine carrée de la taille de l'échantillon.">
                        Entrez une constante, <Italic>C</Italic>, pour satisfaire
                    <InlineUIContainer Style='{StaticResource image_container_style}'>
                            <Image x:Name='formula_11' Source='{StaticResource equation_11}' Style='{StaticResource image_style}' Tag='3.0'>
                            <Image.Height>
                                <MultiBinding Converter='{StaticResource image_size}'>
                                    <Binding Mode='OneWay' ElementName='formula_11' Path='Tag'/>                        
                                    <Binding Mode='OneWay' ElementName='c_textblock' Path='FontSize'/>
                                </MultiBinding>
                            </Image.Height>
                        </Image>
                    </InlineUIContainer>
                </TextBlock>

                <TextBlock  Style="{StaticResource description_textblock_style}"
                            KeyboardNavigation.TabIndex="1">
                        (<Hyperlink AutomationProperties.Name="Plus d'informations sur la constante C">en savoir plus</Hyperlink>)
                </TextBlock>

                <TextBox    Style="{StaticResource entry_textbox_style}"
                            AutomationProperties.LabeledBy="{Binding ElementName=c_textblock}"
                            KeyboardNavigation.TabIndex="0">
                </TextBox>

        </WrapPanel>
</UserControl>

Во время выполнения я смотрю на CurrentCulture, сравниваю его с доступными сгенерированными файлами xaml, передаю файл в XamlReader.Load () и добавляю полученный Usercontrol, где это необходимо. Небольшое приложение, демонстрирующее это, доступно здесь .

0 голосов
/ 09 ноября 2009

Допустим, у вас есть текст:

"Hello, my <b>dear</b> user!"

И вы хотите локализовать этот текст на каком-то другом языке:

"Привет, мой <b>дорогой</b> пользователь!!" 

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

РЕДАКТИРОВАТЬ : Если ваш форматированный текст содержит ссылку на изображение, вам следует локализовать способ, которым ссылка на изображение создается с помощью чего-то вроде этого:

<img src="$current_locale/logo.jpg" />

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

<img src="$get_current_locale_or_default_locale_image(logo.jpg)" />
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...