精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

構建多語言的 WPF 應用

開發 后端
一個局部的WPF應用程序可以在運行時動態地改變UI。第一次運行它是在法語的本地計算機環境中,瞧, il est affiché en Français. 它們都來自同一種語言版本。

下載源代碼 - 84.4 KB

A selection of languages in the one application at runtime

導言

在WPF應用程序中搭建多語言支持(Multilingual Support)是我最近在做的一件事,對于不使用英語的人士而言,此舉提高了程序的可用性。實現起來要完成以下目標:

  • 一個版本容納多種語言. 這就意味著不要創建單獨的英語版本、法語版本、日語版本等等。 許多電子產品(例如電視和數碼相機)在同一模塊中支持多語言。你不需要購買不同模塊或給軟件打補丁來得到與默認設置不同的語言

  • 允許在運行時切換接口語言. 這就是說不需要關閉應用程序并配置操作系統環境,一切都交給安裝器。

  • ***運行選擇合適語言. W應用程序***次運行,就把接口語言設為操作系統的系統語言。這點很有意義--法國用戶喜歡安裝、運行、并馬上使用軟件,而不會再一個不熟悉的應用程序中找到切換語言的地方。

  • 允許UI可拓展以便翻譯,縮減可能的裁剪文本

此外,具體實現不應該隨著用戶界面的增長而越來越來難實現。(這是我覺得最困難的方面。)

所以這篇文章旨在提供一份我開發過程的詳細解決方案的大綱,這基于一些我過去寫過的博客和帖子(這里這里這里)。隨著時間的推移,我將指出例子的相關部分并告訴你它們是如何適配在一起的。

聲明:例子中的文本是使用自動在線翻譯服務生成的。盡管盡了***的努力來確保這是盡可能準確(通過反向翻譯校對),有可能翻譯的內容有不準確或錯誤。特別是當它使用了一個我不清楚的完全不同的寫作系統。

上層概述

這個為WPF應用所設計的實現遵循了一種MVVM(模型-視圖-視圖模型)樣式。語言數據存儲在嵌入式XML文件中,這些文件按照需求原則加載到內存中,即當接口語言發生改變的時候。這就是“模型”的部分。.

“視圖模型”具有將當前語言的語言數據包含到整個WPF應用中的特性。它是XAML文件的集合,XAML文件形成了包含了關聯該語言數據的“視圖”。為了給一個特定的文本元素選擇準確的值,每個關聯都利用具有一個轉換器參數的用戶定制值轉化器來查找文本鍵值。***,用一個用戶定制標記擴展來抽取這個關聯的細節,這樣只有鍵值(即轉換參數)需要指定。

例子

為了說明這個實現在實際中如何工作,我根據這個功能創建一個小的示例應用。這個叫做'RePaver'的應用用于清除路徑標記表達式,并具有基本的翻轉,反轉,轉換和縮放實際幾何圖形(即無需圖層轉換)的功能。在后臺,該應用用正則表達式抽取路徑段落,并就地對每個段落進行轉換。

為了給你有個概念,看如下一個Path表達式的例子,這個表達式一般從導出為XAML格式的矢量圖形文件中得到(這個路徑表達式跟一些我目前經手項目的路徑沒有關系!):

  1. <Path Data="M 470.567,400.914 L 470.578,   
  2.             390.903 L466.551,390.863 L 472.6,384.876 L472.598,400.888 Z" ... /> 

如果你復制黏貼(引號中的)數據表達式到輸入框中并點擊'Go',可以看到如下的輸出:

  1. M 4,16 L 4,6 L 0,6 L 6,0 L 6,16 Z 

在右邊你還能即時看到形象化的"轉換前"和"轉換后"的結果。

你可以任意設置一些選項 - 可以看到這些操作是按照翻轉/反轉 -> 縮放到[根據邊框尺寸] -> 偏移。當然,你可以用不同的語言試一下。

模塊

XML

如上所述,每個組成用戶界面的文本都保存在每種語言的XML文件的本地化表格中, 并把XML文件當做嵌入式資源來編譯。每條text的父元素包含一個鍵屬性用來檢索本地化文本。下面是英語版本定義文件的例子,LangEN.xml:

  1. <LangSettings> 
  2.     <IsRtl>0</IsRtl> 
  3.     <MinFontSize>11</MinFontSize> 
  4.     <HeadingFontSize>16</HeadingFontSize> 
  5.     <UIText> 
  6.         <!-- Menu bar --> 
  7.         <Entry key="TransformLabel">Transform</Entry> 
  8.         <Entry key="LanguageLabel">Language</Entry> 
  9.  
  10.         <!-- Common Operations --> 
  11.         <Entry key="ApplyLabel">Apply</Entry> 
  12.         <Entry key="UndoLabel">Undo</Entry> 
  13.         <Entry key="CancelLabel">Cancel</Entry> 
  14.  
  15.         <!-- Section Headings --> 
  16.         <Entry key="InputLabel">Input</Entry> 
  17.         <Entry key="OutputLabel">Output</Entry> 
  18.         <Entry key="InfoLabel">Info</Entry> 
  19.         <Entry key="TransformPropertiesLabel">Transform</Entry> 
  20.  
  21.         <!-- Item Labels --> 
  22.         <Entry key="FlipRotateLabel">Flip / Rotate</Entry> 
  23.         <Entry key="OffsetLabel">Offset</Entry> 
  24.         <Entry key="ScaleToLabel">Scale To</Entry> 
  25.         <Entry key="DimensionsLabel">Dimensions</Entry> 
  26.         <Entry key="WidthLabel">Width</Entry> 
  27.         <Entry key="HeightLabel">Height</Entry> 
  28.         <Entry key="GoLabel">Go</Entry> 
  29.  
  30.     </UIText> 
  31. </LangSettings> 

在上述英文版本示例中,同樣提到了 theIsRtl, MinFontSize, 和HeadingFontSize元素。字體大小用來決定渲染字體的尺寸,讓字體更易分辨,尤其在顯示日文,韓文和阿拉伯文的時候。IsRtlel元素決定語言是否從右往左讀(阿拉伯文和希伯來語就是這樣)。

注意到語言名稱并沒有出現在上面的XML文件中。這是因為本地化語言名稱放在一個單獨的XML文件中定義, LanguageNames.xml:

  1. <LangNames> 
  2.     <Language code="en">English</Language> 
  3.     <Language code="ar">العربية</Language> 
  4.     <Language code="de">Deutsch</Language> 
  5.     <Language code="el">&Epsilon;&lambda;&lambda;&eta;&nu;&iota;&kappa;ά</Language> 
  6.     <Language code="es">Espa&ntilde;ol</Language> 
  7.     <Language code="fr">Fran&ccedil;ais</Language> 
  8.     <Language code="he">עברית</Language> 
  9.     <Language code="hi">हिन्दी</Language> 
  10.     <Language code="it">Italiano</Language> 
  11.     <Language code="jp">日本語</Language> 
  12.     <Language code="ko">한국어</Language> 
  13.     <Language code="ru">Русский</Language> 
  14.     <Language code="sv">Svenska</Language> 
  15. </LangNames> 

每種語言定義文件的命名遵循這樣一個慣例, 'LangXX.xml'.其中,XX 與兩個字母的 ISO語言代碼相對應,LanguageNames.xml中的每個Language元素也該代碼對應。當然,這一慣例可以拓展或修改為易于處理本地化(如 en-NZ, en-US),甚至改成三字母的ISO語言代碼。

UILanguageDefn類

在語言定義文件中的當前界面語言數據被加載進一個內部類(UILanguageDefn)中是為了被剩下的應用消耗掉。主要的組件是一個<string, string>類型的字典。這個字典包含了從文本鍵到局部的文本值的映射。其它的屬性顯示:IsRtl(是否右對齊),MinFontSize(最小字體大小)和HeadingFontSize的值。

當你使用這個類的時候,局部語言文本會通過調用下面的方法重新取回:

  1. /// <summary>  
  2. /// Gets the localised text value for the given key.  
  3. /// </summary>  
  4. /// <param name="key">The key of the localised text to retrieve.</param>  
  5. /// <returns>The localised text if found, otherwise an empty string.</returns>  
  6. public string GetTextValue(string key)  
  7. {  
  8.     if (_uiText.ContainsKey(key))  
  9.         return _uiText[key];  
  10.  
  11.     return "";  

除此之外,UILanguageDefn類有一個靜態的從語言編碼到局部的語言名稱的映射(這個映射是從LanguageNames.xml中加載進來的),例如,“en”和“English”、“sv”和“Svenska”。這被用來填充到'Language'標簽的可用語言列表中,而且被應用所支持的權威的語言列表過濾。因此,任何不再這個列表的語言不會被界面所顯示。即使有一個語言定義文件或在LanguageNames.xml中有所對應的實體,也不會顯示這個語言。這會在下面的章節中進一步介紹。

#p#

加載數據

類`UILanguageDefn`形成模型的一部分。模型里面的第二個主要的實體就是應用全狀態,`MainWindowModel`。它包含了被整個應用程序使用的`UILanguageDefn`的授權的實例。這是在全部界面中獲取文本元素的邊界的實例。(通過ViewModel)。

當`MainWindowModel`被構造時,在加載當前語言之前,首先會注冊語言列表的授權和從名字為LanguageNames.xml的資源文件中加載本地化語言。下面通過例子讓我們看看它是如何工作的:

  1. public class MainWindowModel  
  2. {  
  3.     private UILanguageDefn _languageMapping;  
  4.  
  5.     public MainWindowModel(int maxWidth, int maxHeight)  
  6.     {  
  7.         RegisterLanguages();  
  8.         LoadLanguageList();  
  9.           
  10.         //Settings are loaded here, where CurrentLanguageCode is decided.  
  11.  
  12.         UpdateLanguageData();  
  13.     }  
  14.       
  15.     public string CurrentLanguageCode  
  16.     {  
  17.         get   
  18.         {  
  19.             // Retrieves the current language code from  
  20.             // the Settings model (abstracted away)  
  21.         }  
  22.     }  
  23.       
  24.     /// <summary> 
  25.     /// Registers the languages by their corresponding ISO code.  
  26.     /// </summary> 
  27.     private void RegisterLanguages()  
  28.     {  
  29.         // Defined in Constants class  
  30.         string[] supportedLanguageCodes =  
  31.         {  
  32.             "en", "ar", "de", "el",   
  33.             "es", "fr", "ko", "hi",   
  34.             "it", "he", "jp", "ru", "sv"    
  35.         };  
  36.           
  37.         foreach(string languageCode in supportedLanguageCodes)  
  38.             UILanguageDefn.RegisterSupportedLanguage(languageCode);  
  39.     }  
  40.  
  41.     /// <summary> 
  42.     /// Loads the list of available languages from the embedded XML resource.  
  43.     /// </summary> 
  44.     private void LoadLanguageList()  
  45.     {  
  46.         // Defined in Constants class  
  47.         string resourcePath = "RePaverModel.LanguageData.LanguageNames.xml";  
  48.           
  49.         System.IO.Stream file =  
  50.                 Assembly.GetExecutingAssembly().GetManifestResourceStream(resourcePath);  
  51.  
  52.         XmlDocument languageNames = new XmlDocument();  
  53.         languageNames.Load(file);  
  54.  
  55.         UILanguageDefn.LoadLanguageNames(languageNames.DocumentElement);  
  56.     }  
  57.  
  58.     /// <summary> 
  59.     /// Updates the UI language data from that  
  60.     /// defined in the corresponding language file.  
  61.     /// </summary> 
  62.     /// <returns> 
  63.     public bool UpdateLanguageData()  
  64.     {  
  65.         string languageCode = CurrentLanguageCode;  
  66.         if (String.IsNullOrEmpty(languageCode)) return false;  
  67.  
  68.         //This follows a convention for language definition files  
  69.         //to be named &apos;LangXX.xml&apos; (or &apos;LangXX-XX.xml&apos;)  
  70.         //where XX is the ISO language code.  
  71.         string resourcePath =   
  72.           String.Format(Constants.LanguageDefnPathTemplate, languageCode.ToUpper());  
  73.         System.IO.Stream file =  
  74.             Assembly.GetExecutingAssembly().GetManifestResourceStream(resourcePath);  
  75.  
  76.         XmlDocument languageData = new XmlDocument();  
  77.         languageData.Load(file);  
  78.  
  79.         _languageMapping = new UILanguageDefn();  
  80.         _languageMapping.LoadLanguageData(languageData.DocumentElement);  
  81.  
  82.         return true;  
  83.     }  

你可能注意到上面的代碼提到了第三個主體 - 設置狀態。在眾多可在運行時調整的設置中,正是這個狀態存儲了當前正被使用的接口語言。大多數的設置項都在應用程序關閉后保存在磁盤中,當程序再次打開時就重新加載出來。

然而,如果應用程序是***次打開(沒有設置文件存在),那么這些設置就會被設定為默認狀態。對于語言來說,英語是默認的,但這并不是用戶友好(user-friendly)的。所以呢,我們就這樣檢索當前系統語言:

  1. CultureInfo.CurrentCulture.TwoLetterISOLanguageName; 

找到相應的語言后,如果應用程序不支持該語言,就讓英語作為默認語言。這樣,只要你的本地語言受支持,UI就會在你程序***次運行時顯示該語言。在Setting model hierarchy中,有如下代碼

  1. public LanguageSettings()  
  2. {  
  3.     // Initialise the default language code.  
  4.     // In most cases this will be overwritten by the  
  5.     // restored value from the saved settings, or that of the current culture.  
  6.     _uiLanguageCode = Constants.DefaultLanguageCode;     //"en"  
  7.  
  8.     string languageCode = CultureInfo.CurrentCulture.TwoLetterISOLanguageName;  
  9.  
  10.     // If the system language is supported, this will  
  11.     // ensure that the application first loads  
  12.     // with the UI displayed in that language.  
  13.     if (UILanguageDefn.AllSupportedLanguageCodes.Contains(languageCode))  
  14.         _uiLanguageCode = languageCode;  
  15. }  

這個類中的另一種方法,姑且叫做后者吧 (有用戶設置文件存在的時候使用),它會提取保存在文件中的設置項的值,并把它復寫到_uiLanguageCode.

視圖模型

這里出現了一個MVVM實現方法,它不同于WPF和Silverlight應用程序中的Model-View-Presenter(MVP).在MVP模式中,我們需要一個Presenter把當前語言的定義(或單個的本地化后的文本)傳給視圖(View),由視圖負責UI中文本的顯示與更新。考慮到我們在使用WPF,文本的更新可以很容易地通過數據綁定來實現;考慮到語言定義要在整個應用程序(組件或窗體)中使用,我們需要一個共享類來保存當前語言屬性,這樣當進行數據綁定時,就能使該屬性在UI的任何一部分檢索出來。

在MVVM模式中,這個共享類同其他視圖模型(例如MainWindowViewModel)一道,將成為組成視圖模型層的一部分。CommonViewModel這個類是作為單例模式(Singleton)來實現,這樣靜態實例屬性Current就可以作為一個綁定的源屬性來賦值了。非靜態屬性則通過綁定的Path屬性來引用。還有一點很重要,ViewModel實現了INotifyPropertyChanged的接口,以致UI能在源數值發生改變時自動更新綁定。

這里是綁定到UI的CommonViewModel屬性,UILanguageDefn類給出了數據的定義:

  1. /// <summary>  
  2. /// Gets or sets the language definition used by the entire interface.  
  3. /// </summary>  
  4. /// <value>The language definition.</value>  
  5. public UILanguageDefn LanguageDefn  
  6. {  
  7.     get { return _languageDefn; }  
  8.     set  
  9.     {  
  10.         if (_languageDefn != value)  
  11.         {  
  12.             _languageDefn = value;  
  13.             OnPropertyChanged("LanguageDefn");  
  14.             OnPropertyChanged("HeadingFontSize");  
  15.             OnPropertyChanged("MinFontSize");  
  16.             OnPropertyChanged("IsRightToLeft");  
  17.         }  
  18.     }  
  19. }  
  20.  
  21. public double HeadingFontSize  
  22. {  
  23.     get   
  24.     {  
  25.         if (_languageDefn != null)  
  26.             return (double)_languageDefn.HeadingFontSize;  
  27.  
  28.         return (double)UILanguageDefn.DefaultHeadingFontSize;  
  29.     }  
  30. }  
  31.  
  32. public double MinFontSize  
  33. {  
  34.     get  
  35.     {  
  36.         if (_languageDefn != null)  
  37.             return (double)_languageDefn.MinFontSize;  
  38.  
  39.         return (double)UILanguageDefn.DefaultMinFontSize;  
  40.     }  
  41. }  
  42.  
  43. public bool IsRightToLeft  
  44. {  
  45.     get  
  46.     {  
  47.         if (_languageDefn != null)  
  48.             return _languageDefn.IsRightToLeft;  
  49.  
  50.         return false;  
  51.     }  
  52. }  

MainWindowViewModel處在ViewModel架構最前端, 負責在MainWindowModel值發生變化時,更新CommonViewModel中的當前語言:

  1. /// <summary>  
  2. /// Refreshes the UI text to display in the current language.  
  3. /// </summary>  
  4. public void RefreshUILanguage()  
  5. {  
  6.     _model.UpdateLanguageData();  
  7.     CommonViewModel.Current.LanguageDefn = _model.CurrentLanguage;  
  8.  
  9.     //Notify any other internal logic to prompt a refresh (as necessary)  
  10.     if (LanguageChanged != null)  
  11.         LanguageChanged(thisnew EventArgs());  
  12. }  

#p#

視圖

正如我所提到的,本地化文本通過數據綁定顯示到視圖中。然而WPF自身并不知道如何處理UILanguageDefn類,更不用說提取合適的本地化文本值。這也是***一個難題。

值轉換器

請記住,CommonViewModel.Current.LanguageDefn是一個UILaunguageDefn,不是TextBlock的Text屬性期待的一個字符串。因此,此時需要一個值轉換器來完成這項轉換工作。這個值轉換器使用ConverterParameter來指定創建查找關鍵字,用來恢復來自UILanguage實例中局部符合條件的文本。記住,當接口改變了,UILanuageDefn也改變。

這項工作的優點在于對每一段局限在接口當中的文本,符合條件的元素需要被添加到language XML文件,確保ConverterParameter和元素名稱匹配。此外不需要定義任何額外的屬性——不管是在視圖層,UILanguageDefn,還是在模型層的其他部分。

這個converter相對簡單. 只需在類級別上指定 IValueConverter (在System.Windows.Data中)的 ValueConversion 屬性:

  1. [ValueConversion(typeof(UILanguageDefn), typeof(string))] 

并且實現類似如下的函數 Convert :

  1. public object Convert(object value, Type targetType,   
  2.                       object parameter, CultureInfo culture)  
  3. {  
  4.     string key = parameter as string;  
  5.     UILanguageDefn defn = value as UILanguageDefn;  
  6.  
  7.     if (defn == null || key == null) return "";  
  8.  
  9.     return defn.GetTextValue(key);  
  10. }  

綁定

現在我們獲得了了一個 value converter, 我們可以將它放置在一個 Binding 表達式中:

  1. <TextBlock Text="{Binding Path=LanguageDefn,  
  2.     Converter={StaticResource UIText}, ConverterParameter=ApplyLabel,  
  3.     Source={x:Static vm:CommonViewModel.Current}}" /> 

如果想要它工作, 這個 XML 的 命名空間必須設置為 vm(指向 ViewModel的命名空間),并且 UIText 的資源需要被定義 (假設conv 是這個 value converter 的 XML 的命名空間):

  1. zlt;conv:UITextLookupConverter x:Key="UIText" /> 

簡單明了——自定義標記擴展

如果你當前的狀態(像我一樣)又想要愉快的方式,在大多數的XAML文件中的長綁定表達式里,你發現它變得乏味,是同一樣東西的重復。甚至不考慮重命名類或者把屬性作為重構的一部分!

當然,有一種方式能使其更簡潔,考慮到這些綁定之間的唯一變化就是ConverterParameter。解決方案是使用使用自定義標記擴展。

為了做到這一點,自定義標記擴展是一個簡單的類,它派生自MarkupExtension(在System.Windows.Markup),按照慣例被命名為[name]Extension。在其核心處,關鍵點是需要重載ProvideValue方法。但是這該怎么做呢?

自定義標記拓展的重點就是在XAML中寫下類似這樣的代碼:

  1. <TextBlock Text="{ext:LocalisedText Key=ApplyLabel}" /> 

因此,自定義拓展被稱作LocalisedTextExtension,并添加一個Key,它的類型是public string.因為在后臺中,綁定一直處于使用狀態,所以我創建了一個private 綁定域,并從構造器中實例化它 :

  1. public LocalisedTextExtension()  
  2. {  
  3.     _lookupBinding = UITextLookupConverter.CreateBinding("");  
  4. }  

而靜態的CreateBinding方法定義在值轉換器(value converter)中:

  1. public static Binding CreateBinding(string key)  
  2. {  
  3.     Binding languageBinding = new Binding("LanguageDefn")  
  4.     {  
  5.         Source = CommonViewModel.Current,  
  6.         Converter = _sharedConverter,  
  7.         ConverterParameter = key,  
  8.     };  
  9.     return languageBinding;  

所以定義好了Binding后,可以通過ConverterParameter參數來獲取和設置Key屬性的值。這也使得ProvideValue方法可以大展身手:

  1. public override object ProvideValue(IServiceProvider serviceProvider)  
  2. {  
  3.     return _lookupBinding.ProvideValue(serviceProvider);  
  4. }  

而一個Binding是一個MarkupExtension,所以它有自己的可以調用的ProvideValue方法。

#p#

Rinse and Repeat - 字體大小與流方向

某些語言的字符集包含十分復雜的圖形元素,以致在拉丁文可以辨認的字符大小,用來顯示這些語言的時候,變得模糊不清了。你注意到CommonViewModel提供了HeadingFontSize和MinFontSize屬性。這就為本地化標題和剩余的本地化文本相應地提供了字體大小。例如日文的字體大小就大于英文。

幸運的是,使用類似下面的這個模式就可以把上述的文字尺寸綁定到共享的樣式中,而不需要值轉換器:

  1. <Style TargetType="{x:Type TextBlock}">  
  2.     <Setter Property="FontSize" Value="{Binding Path=MinFontSize,  
  3.             Source={x:Static vm:CommonViewModel.Current}}" />  
  4.     <!-- Remaining setters ... -->  
  5. </Style>  

下圖顯示的是兩個同樣界面不同語言下的差異:

Side-by-side comparison of languages with varying font size 

也有一些語言是從右向左讀的,例如阿拉伯語和希伯來語。為了讓UI正確的定位到這些語言,反轉接口是有意義的,否則會帶來一些混淆,如果在使用程序的時候讀取的順序和邏輯的順序不一致。

Partial localisation of right-to-left languages

幸運的是,WPF有一個方便的屬性可以完成反轉整個UI的艱苦工作:

FrameworkElement.FlowDirection

是什么讓這個功能相當強大,我只需要綁定一個包含在主窗口內的根級別控件,因為這個值是由它下面的每個FrameworkElement的在視覺層次繼承。綁定僅僅需要查看CommonViewModelIsRightToLeft屬性,轉換到(通過其他的值轉換器)FlowDirection的枚舉值。自定義的標記擴展被創建,遵循以前類似的模板,簡化為XAML:

  1. <Window x:Class="RePaver.UI.MainWindow" ... >  
  2.     <DockPanel FlowDirection="{ext:LocalisedFlowDirection}">  
  3.         <!-- Contents -->  
  4.     </DockPanel>  
  5. </Window>  

鑒于到上述功能的強大,這里仍然要考慮一些陷阱和要點:

  • 自定義面板自動反轉布局,所以你不需要創建一個IsRevered屬性(或者類似的)或者按照你的估算調整ArrangeOverride。

  • 位圖和形狀(如線路)是反轉的。如果您想要保留這些,呈現獨立的流向(如公司的logo或者商標),那么你需要重寫FlowDirection,設置它為LeftToRight。

  • 如果接口有RightToLeft的FlowDirection,而元素(如Image)具有LeftToRight的FlowDirection,那么元素的Margin會以RightToLeft的方式展示。由于Padding展示在元素內部可視層次,所以一個padding將會以LeftToRight的方式展示。

  • TextBoxes包含語言恒定的數據,應當將FlowDirection設置為LeftToRight。理想情況下,此屬性應設置為盡量減少重復并保證一致性的風格。

所以,下面就是趕時髦的“處理后”的截圖:

More complete localisation of right-to-left languages

注意路徑,旋轉選擇控件,輸入輸出文本框是以從左至右的方式展示,這與語言無關。這是因為這些元素是特定的問題區域,如果它們以從右至左的方式展示,就沒有道理了(可能會引起誤解)。

總結

現在明白了——一個局部的WPF應用程序可以在運行時動態地改變UI。***次運行它是在法語的本地計算機環境中,瞧, il est affich&eacute; en Fran&ccedil;ais. 它們都來自同一種語言版本。

***一個要點需要注意,這里不做詳細介紹,整個UI布局以流體方式布局,這樣的布局會自動調整以適應內容。 而不是顯式地設置寬度和高度, 網格的行/列定義,等等。這些都是“自動”為左的,同時還可以定義最小和***值。這是很普通的實例中***的一個(而不是特定的本地化), 但當切換語言的時候,不允許這樣的實例真的顯示出來。

后記

軟件開發中本地化是一個熱門的話題,理所當然,我也不是唯一一個寫這方面的人。事實上,我也發現了一些人在做同樣的事:

  • Sebastian Przybylski (article) 也把UI文本存儲在XML文件作為嵌入資源,而把XAML直接綁定到XML資源上而不是通過ViewModel.

  • David Sleeckx (article) 使用自定義標記拓展來檢索本地緩存的翻譯文本,或者調用Google語言API來實現實時翻譯。

  • 'SeriousM' 在CodePlex上更新了 WPF本地化拓展 . 它是通過提取資源文件/資源程序集中的本地化文本(或其他值)來實現的。

顯然,實現WPF程序的本地化有很多種選擇,它們并不互斥。根據你的權衡,我所提到的實現方法僅適用于你程序的部分,另一部分則會出現在其他的地方。所以你要根據你的需求,隨意調整實現方法。

英文原文:Building Multilingual WPF Applications

譯文來自:http://www.oschina.net/translate/building-multilingual-wpf-applications

責任編輯:林師授 來源: 開源中國社區 編譯
相關推薦

2009-07-17 10:02:29

WPF程序多語言支持

2023-06-29 07:27:26

知識圖譜Shopee

2014-04-16 14:50:20

Spark

2025-07-24 07:42:08

2009-08-25 10:44:50

C#實現多語言

2012-04-19 11:40:21

Titanium

2011-08-05 17:54:33

Cocoa Touch 多語言

2021-06-29 21:48:32

開源語言架構

2021-09-07 10:17:35

iOS多語言適配設計

2024-05-09 08:14:09

系統設計語言多語言

2011-07-26 10:09:08

iPhone 多語言 國際化

2022-08-09 07:22:15

語言數據庫程序

2025-08-11 07:00:00

2013-10-16 15:50:01

iOS優化本地化

2023-08-04 10:18:15

2020-04-14 09:50:02

2019-12-05 16:00:15

Vim插件編程文本編輯器

2009-08-31 17:13:09

2009-08-03 17:33:01

ASP.NET多語言支

2021-07-24 11:41:42

前端開發技術
點贊
收藏

51CTO技術棧公眾號

奇米精品一区二区三区四区| 国产日韩中文在线中文字幕| 99久久综合国产精品| 性色av一区二区三区| 超碰97人人干| 激情欧美一区二区三区黑长吊| 日韩一区欧美一区| 国产日韩亚洲精品| 中文字幕 欧美激情| 国产综合亚洲精品一区二| 亚洲美女性生活视频| 三日本三级少妇三级99| 午夜久久中文| 亚洲欧美国产77777| 久久久一本精品99久久精品66 | 黑人精品一区二区| 久久久久久亚洲精品杨幂换脸| 久久精品小视频| 超碰97人人干| 在线精品自拍| 欧美日韩综合不卡| 国产综合av在线| 看黄网站在线观看| www亚洲一区| 99视频在线| 91精品国自产| 亚洲自啪免费| 国内精品400部情侣激情| 蜜桃av免费观看| 亚洲裸色大胆大尺寸艺术写真| 欧美一区二区私人影院日本| 国产1区2区在线| 91桃色在线| 亚洲欧美区自拍先锋| 日本一区二区久久精品| 少妇高潮久久久| 国产精品影音先锋| 国产美女91呻吟求| 国产污视频网站| 国产精品久久久亚洲一区| 久久69精品久久久久久久电影好| 污污视频网站在线免费观看| 亚洲丁香日韩| 日韩经典第一页| 精品人妻伦一二三区久| 无人区乱码一区二区三区| 欧美日韩精品一区二区天天拍小说 | av资源久久| 亚洲欧美999| 亚洲色图14p| 欧美美女在线直播| 亚洲国产高清自拍| 天天躁日日躁狠狠躁av麻豆男男| 日韩一级淫片| 日韩免费电影一区| 韩国三级hd中文字幕有哪些| 精品视频在线观看免费观看| 欧美一区二区三区四区五区 | 国产一区二区三区观看| 国产精品一香蕉国产线看观看 | 国模私拍视频在线播放| 樱桃视频在线观看一区| 最近免费观看高清韩国日本大全| 精品自拍一区| 亚洲精品视频一区二区| 国产一区二区三区在线免费| 人妖欧美1区| 亚洲一区免费观看| 黄色激情在线视频| 色在线中文字幕| 一本到一区二区三区| 九九视频精品在线观看| 亚洲人体在线| 日韩你懂的在线观看| 亚洲天堂2024| 国产日产精品一区二区三区四区的观看方式 | 日韩精品视频网址| 日韩区一区二| 亚洲精品视频免费| 国产又黄又粗的视频| 日韩在线视频精品| 欧美另类暴力丝袜| 91美女免费看| 麻豆精品一二三| 亚洲iv一区二区三区| 亚洲国产综合一区| 久久久久国产精品麻豆| 亚洲日本欧美在线| 免费毛片在线看片免费丝瓜视频| 岛国精品视频在线播放| 向日葵污视频在线观看| 视频在线一区| 亚洲小视频在线| 人妻少妇精品一区二区三区| 国产视频一区三区| 91精品久久久久久久久久另类| www香蕉视频| 国产午夜精品久久久久久久| 欧美日韩亚洲国产成人| 一个人看的www视频在线免费观看| 欧美日韩一区成人| 亚洲性图第一页| 精品一二三区| 久久免费福利视频| 中文在线a天堂| www.66久久| 中文有码久久| 中文字幕在线中文字幕在线中三区| 91麻豆精品国产91久久久资源速度 | 国产成人综合亚洲91猫咪| 久久66热这里只有精品| 黄色在线论坛| 欧美在线观看一区| 69xxx免费视频| 欧美激情电影| 日本精品久久久久影院| www.99视频| 国产欧美日韩精品a在线观看| 亚洲一区二区三区av无码| 欧美aaa级| 日韩精品在线私人| 久久久久久久久艹| 激情久久五月天| 日韩高清国产精品| 七七成人影院| 日韩欧美一级特黄在线播放| 色偷偷男人天堂| 日韩中文欧美在线| 精品一区日韩成人| 变态调教一区二区三区| 欧美一级片在线观看| 懂色av蜜臀av粉嫩av永久| 欧美专区在线| 精品欧美一区二区久久久伦| 日韩专区av| 日韩午夜激情视频| 日韩av手机在线免费观看| 日本伊人午夜精品| 日韩欧美一区二区三区四区| 草草视频在线| 亚洲成人黄色网| 久久免费视频精品| 国产福利一区在线| 麻豆视频传媒入口| 2020国产精品小视频| 丝袜亚洲另类欧美重口| 九九九九免费视频| 深夜福利一区二区三区| 久久综合五月天| 国产乱码精品一区二区| 国产精品高潮呻吟久久| 天天干天天操天天做| 日韩国产欧美一区二区| 国产欧美日韩高清| 麻豆最新免费在线视频| 在线综合亚洲欧美在线视频| 国产十六处破外女视频| 国产精品综合一区二区三区| 永久免费网站视频在线观看| 警花av一区二区三区| 美女视频黄免费的亚洲男人天堂| 国产精品视频在线观看免费| 亚洲日本电影在线| 制服.丝袜.亚洲.中文.综合懂| 欧美日韩久久| 国产日韩欧美精品| 偷拍自拍在线看| 一区二区三区视频观看| 97精品人妻一区二区三区香蕉| 成人欧美一区二区三区1314 | 岛国一区二区三区| 久艹在线免费观看| 亚洲第一论坛sis| 国产精品美女网站| 黄在线免费看| 亚洲福利在线视频| 国产高潮久久久| 国产免费观看久久| 国产探花在线观看视频| 黄色av一区| 欧美精品一区二区三区在线看午夜 | 国产精品爱久久久久久久| eeuss影院在线观看| 51久久夜色精品国产麻豆| 久久久.www| 久久综合九色欧美综合狠狠| 日韩欧美国产片| 极品av少妇一区二区| 欧美日韩一区二区视频在线观看| 日韩精品第二页| 久久久久久久久久久av| 黄色国产在线| 日韩欧美一级在线播放| 中文字幕在线天堂| 一区二区在线免费| 这里只有久久精品| 国产精品99久久久久久似苏梦涵 | 精品国产一区a| 色老头在线视频| 亚洲在线免费播放| www.日本高清视频| 波多野结衣在线一区| 久久综合伊人77777麻豆最新章节| 欧美精品偷拍| 日韩精品在在线一区二区中文| 欧美午夜在线播放| 国产精品com| av福利在线导航| 国产一区二区三区视频免费| 亚洲精品国产精品乱码不卡| 欧美三级电影在线观看| 日韩av综合在线| 亚洲伦在线观看| 神马久久久久久久久久久| 成人丝袜高跟foot| 国产高清av片| 日本伊人午夜精品| 国产精品宾馆在线精品酒店| 亚洲欧美一级二级三级| 无码免费一区二区三区免费播放 | 亚洲第一网站男人都懂| 91精品人妻一区二区三区果冻| 欧美视频在线免费| 久久免费公开视频| 亚洲色图另类专区| 性少妇xx生活| 国产日韩欧美一区二区三区乱码| youjizz.com国产| 国产盗摄一区二区三区| 中文字幕亚洲乱码| 日本视频在线一区| 亚洲爆乳无码专区| 亚洲综合社区| 免费国产黄色网址| 亚洲午夜在线| 欧美人与动牲交xxxxbbbb| 99久久婷婷| 一区二区三区四区在线视频| 日韩午夜电影网| 神马影院午夜我不卡影院| 亚洲电影男人天堂| 久久久久久国产精品mv| 美女视频免费精品| 国产日韩亚洲精品| 秋霞蜜臀av久久电影网免费| 国产另类自拍| 超碰精品在线| 韩国精品一区二区三区六区色诱| 国产精东传媒成人av电影| 91免费看网站| 日韩精品久久久久久久软件91| 亚洲xxxx做受欧美| 日韩一区二区三区色| av资源一区二区| 91亚洲无吗| 好看的日韩精品视频在线| 另类图片第一页| 美女黄毛**国产精品啪啪| 久久av免费| 日本不卡一区二区三区在线观看| 精品国产一区探花在线观看| 日韩在线国产| 欧美激情黄色片| 成人在线免费高清视频| 欧美日韩综合| 无罩大乳的熟妇正在播放| 久久婷婷激情| 99热这里只有精品在线播放| 国产原创一区二区三区| 日本少妇一级片| 99在线视频精品| xxxx日本免费| 国产精品免费视频网站| www日韩在线| 亚州成人在线电影| 国产一区二区视频网站| 欧美日韩久久一区二区| 亚洲黄色小说网址| 亚洲欧美国产日韩天堂区| 在线免费看黄| 欧美激情a在线| 在线中文字幕播放| 国产综合色香蕉精品| 综合成人在线| 欧洲精品国产| 欧美一区国产在线| 人妻精品无码一区二区三区 | 亚洲国产va精品久久久不卡综合| 免费观看一区二区三区毛片| 欧美伊人久久大香线蕉综合69| 99久久亚洲精品日本无码| 亚洲第一男人av| 成人高清免费在线播放| 色综合91久久精品中文字幕 | 国产欧美精品一区二区三区介绍| 欧美成年网站| 欧美性大战久久久久| 亚洲理论电影网| 欧美成人xxxxx| 国产在线精品一区二区三区不卡 | 香蕉视频一区| 日本xxxxx18| 日一区二区三区| 制服.丝袜.亚洲.中文.综合懂| 国产欧美一区二区精品性色超碰| 九九免费精品视频| 欧美午夜一区二区| 熟妇人妻av无码一区二区三区| 中文字幕亚洲一区二区三区| 蜜桃av.网站在线观看| 91精品黄色| 日韩一区电影| 久久久久狠狠高潮亚洲精品| 国产精品系列在线观看| 特级西西人体高清大胆| 岛国av一区二区在线在线观看| 国产福利免费视频| 中文精品99久久国产香蕉| 免费在线小视频| 99理论电影网| 亚洲色图二区| 亚洲欧美视频二区| 久久久99久久| 亚州国产精品视频| 欧美变态tickle挠乳网站| 黄色成人影院| 国产日韩精品一区二区| 精品国产一区二区三区久久久樱花 | 欧美三区在线观看| 欧美男男同志| 国自产精品手机在线观看视频| 欧美a级大片在线| 国产精品无码乱伦| 人人爽香蕉精品| 色一情一交一乱一区二区三区| 五月天欧美精品| 天堂在线视频免费观看| 欧美激情亚洲另类| 都市激情亚洲欧美| 91传媒免费视频| 国产精品99久久久久久宅男| 国产午夜精品理论片在线| 欧美日韩一区不卡| 在线免费黄色| 国产精品爽爽爽爽爽爽在线观看| 精品久久不卡| 日本成人黄色网| 国产日产精品一区| 中文字幕日本人妻久久久免费| 国产一区二区三区网站| 成人va天堂| 五月天久久综合网| 久久精品国产**网站演员| 国产馆在线观看| 欧美日韩黄色影视| 黄色免费在线观看| 91九色在线观看| 精品91在线| 国产二级一片内射视频播放| 欧美日韩国产麻豆| 青青操在线视频| 国产精品久久久久久久久久ktv| 日韩免费视频| 欧洲在线免费视频| 亚洲一区在线观看视频| 五月激情婷婷网| 日本在线观看天堂男亚洲| 国产影视一区| 天天干天天草天天| 亚洲男人的天堂在线观看| www.成人免费视频| 97精品欧美一区二区三区| 亚洲精品456| mm131亚洲精品| 亚洲精品美腿丝袜| 姝姝窝人体www聚色窝| 国产精品高潮粉嫩av| 99热在线成人| 亚洲午夜久久久久久久久| 色综合久久88色综合天天免费| 91caoporm在线视频| 成人xxxxx色| 性一交一乱一区二区洋洋av| youjizz亚洲女人| 精品欧美一区二区在线观看| 亚洲精品日产| 四虎影院一区二区| aaa亚洲精品| 一级黄色片在线观看| 久久久久久网站| 欧美r级电影| 天天躁日日躁狠狠躁免费麻豆| 色婷婷国产精品综合在线观看| 搞黄网站在线观看| 欧美精品二区三区四区免费看视频 | 精品国产一区探花在线观看| 九九九久久久久久久| 狠狠色噜噜狠狠狠狠97| 黄色av免费在线| 日韩视频精品|