2010-12-02 15 views
13

En mi aplicación WPF, me gustaría mostrar algo que se parece a esto:¿Cómo puedo hacer que un enlace de datos de ancho de texto formateado sea localizable?

usuario Bob ha cerrado la sesión en 22:17.

Donde "Bob" y "22:17" son valores enlazados a datos.

La manera obvia de hacerlo sería utilizar un StackPanel con múltiples TextBlock niños, algunos de ellos los datos ligados:

<StackPanel Orientation="Horizontal"> 
    <TextBlock Text="The user"/> 
    <TextBlock Text="{Binding Path=Username}" TextBlock.FontWeight="Bold" /> 
    <TextBlock Text="has logged off at"/> 
    <TextBlock Text="{Binding Path=LogoffTime}" TextBlock.FontWeight="Bold" /> 
</StackPanel/> 

Esto funciona, pero es feo. Se supone que el programa está localizado en diferentes idiomas, y que tiene cadenas separadas para "El usuario" y "se ha desconectado en" es un destinatario para el desastre de localización.

Idealmente, me gustaría hacer algo como esto:

<TextBlock> 
    <TextBlock.Text> 
     <MultiBinding StringFormat="{}The user <Bold>{0}</Bold> has logged off at <Bold>{1}</Bold>"> 
      <Binding Path="Username" /> 
      <Binding Path="LogoffTime" /> 
     </MultiBinding> 
</TextBlock> 

Así que el traductor vería una oración completa The user <Bold>{0}</Bold> has logged off at <Bold>{1}</Bold>. Pero eso no funciona, por supuesto.

Esto tiene que ser un problema común, ¿cuál es la solución correcta para esto?

Respuesta

4

nunca he intentado hacer algo como esto antes, pero si tuviera que probablemente probar y utilizar un convertidor que lleva el MultiBinding y lo rompe y devuelve un StackPanel de las piezas

Por ejemplo , la unión sería algo así como:

<Label> 
    <Label.Content> 
      <MultiBinding Converter={StaticResource TextWithBoldParametersConverter}> 
       <Binding Source="The user {0} has logged off at {1}" /> 
       <Binding Path="Username" /> 
       <Binding Path="LogoffTime" /> 
      </MultiBinding> 
    </Label.Content> 
</Label> 

y el convertidor haría algo así

public class TextWithBoldParametersConverter: IMultiValueConverter 
{ 
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) 
    { 
     // Create a StackPanel to hold the content 
     // Set StackPanel's Orientation to Horizontal 

     // Take values[0] and split it by the {X} tags 

     // Go through array of values parts and create a TextBlock object for each part 
      // If the part is an {X} piece, use values[X+1] for Text and make TextBlock bold 
      // Add TextBlock to StackPanel 

     // return StackPanel 
    } 
} 
12

El problema que veo es que quiere un solo String pero con una presencia de UI diferente en el String.

Una opción podría ser descartar la necesidad de negrita y simplemente colocar el único String dentro del archivo Resources.resx. A continuación, se hará referencia a ese String en la propiedad a la que está vinculado el TextBlock, devolviendo los valores según sea necesario; {0} y {1} cuando corresponda.

Otra opción podría ser devolver un conjunto de valores de Run dentro del TextBlock. No se puede obligar a un Run fuera de la caja en 3,5 sin embargo yo creo que puedas en 4.

  <TextBlock> 
        <Run Text="The user "/><Run FontWeight="Bold" Text="{Binding User}"/><Run Text="has logged at "/><Run FontWeight="Bold" Text="{Binding LogoffTime}"/> 
      </TextBlock> 

La última opción se puede encontrar here y supone la creación de un enfoque más dinámico para el concepto Run, lo que le permite Ate sus valores y luego vuélvalos a un String.

0

es poco probable que sea hay una única solución a este problema porque hay muchas maneras diferentes en que puede manifestarse, y hay muchas maneras diferentes de resolverlo.

Si usted es Microsoft, la solución es poner todas sus plantillas en un diccionario de recursos, crear un proyecto que pueda usar Expression Blend para presentarlas, y luego hacer que sus traductores trabajen con Blend (y probablemente alguien que pueda ayudarlos a usarlo) para traducir el texto en el diccionario de recursos, reordenando los elementos en la plantilla donde existen diferencias idiomáticas en el orden de las palabras. Esta es, por supuesto, la solución más costosa, pero tiene la ventaja de tener problemas de formato (como que alguien olvidó que el texto en francés ocupa aproximadamente un 20% más de espacio que el texto en inglés) resuelto al mismo tiempo que se traduce la interfaz de usuario. También tiene la ventaja de que maneja todas las presentaciones de texto en la interfaz de usuario, no solo las presentaciones que se crean al juntar bloques de texto.

Si realmente solo va a necesitar arreglar montones de bloques de texto, puede crear una representación XML simple de texto marcado, y usar XSLT para crear su XAML desde un archivo en ese formato. Por ejemplo, algo como:

<div id="LogoutTime" class="StackPanel"> 
    The user 
    <strong><span class="User">Bob</span><strong> 
    logged out at 
    <strong><span class="Time">22:17</span></strong> 
    . 
</div> 

Por una coincidencia asombrosa, que formato de marcado es uno que también se puede ver en un navegador web, por lo que puede ser editado con una muy amplia gama de herramientas y corregir sin usar, por ejemplo, Expression Blend. Y es relativamente sencillo traducir de nuevo a XAML:

<xsl:template match="div[@class='StackPanel']"> 
    <DataTemplate x:Key="{@id}"> 
     <StackPanel Orientation="Horizontal"> 
     <xsl:apply-templates select="node()"/> 
     </StackPanel> 
    </DataTemplate> 
</xsl:template> 

<xsl:template match="div/text()"> 
    <TextBlock Text="{.}"/> 
</xsl:template> 

<xsl:template match="strong/span"> 
    <TextBlock FontWeight="Bold"> 
     <xsl:attribute name="Text"> 
     <xsl:text>{Binding </xsl:text> 
     <xsl:value-of select="@class"/> 
     <xsl:text>}</xsl:text> 
     </xsl:attribute> 
    </TextBlock> 
</xsl:template> 
Cuestiones relacionadas