Localization یا بومی سازی برنامه های WPF با استفاده از فایلهای RESX

سه شنبه 1 دی 1394

در این مقاله بومی سازی (تعیین زبان نمایشی برنامه) در برنامه های WPF را بررسی خواهیم کرد . برای بومی سازی روش های مختلفی وجود دارد که در اینجا قصد داریم فرمت RESX را معرفی کنیم که اجازه می دهد خواص یا ویژگیهای WPF را از منابع RESX تعبیه شده واکشی کرده و استفاده کنیم.

  Localization  یا بومی سازی  برنامه های  WPF با استفاده از فایلهای  RESX

در این مقاله بومی سازی (تعیین زبان نمایشی برنامه با توجه به نیاز کاربر )  در برنامه های  WPF را بررسی خواهیم کرد . برای بومی سازی روش های مختلفی وجود دارد که  در اینجا قصد داریم فرمت RESX را معرفی کنیم که اجازه می دهد خواص یا ویژگیهای  WPF را از منابع RESX تعبیه شده واکشی کرده و استفاده کنیم.

منابع با استفاده از فایل  RESX تعبیه شده میتوانند بومی شوند. روش مشابه به بومی سازی Windows Form  است و منابع مربوط به هر پنجره  WPF یا کنترل در فایلهای RESX جداگانه ذخیره می شوند. در برنامه های بزرگ که تعداد زیادی پنجره وجود دارد ، مدیریت  آن تعداد منبع در یک  فایل مشکل می شود و بومی سازی در این پروژه ها بسیار اهمیت پیدا میکند.

هر خاصیت  WPF می تواند با استفاده از فایل های RESX   بومی سازی شود. می توان از آنها برای محلی کردن تصاویر ، مکان ها ، سایزها و ویژگی های دیگر Layout استفاده کرد. پشتیبانی   Built-in تعریف آیکون برای پنجره ها ، منو ها و نوار ابزار  را بسیار آسان کرده است .

افزونه های RESX به شما اجازه می دهند تا یک مقدار دوم یا مجددی برای خاصیت هایی که استفاده میکنید قرار دهید تا زمانی که اگر منابع نتوانستند بارگذاری شوند از آن مقادیر استفاده شود.

استفاده از افزونه  RESX

برای استفاده از این افزونه باید منبع ان را به پروژه خود اضافه کنید (infralution.Localization.Wpf) . با این کار می توانید از ResxExtension در فایل  XAML خود استفاده کنید. به شما اجازه تعریف مقادیر خواص XAML را می دهد. ResxExtention از کلاس MarkupExtention  مشتق می شوند و خواص را با داده های بازیابی شده از منبع RESX تعبیه شده ارزیابی میکنند. برای مثال Markup زیر خاصیت  Text از یک  TextBlock را به  MyText از منبع RESX  تعبیه شده به نام MyApp.TestWindow تنظیم میکند:

<TextBlock Text="{Resx ResxName=MyApp.TestWindow, Key=MyText}"/>

اگر هنوز منبع را ایجاد نکرده باشید ، در WPF designer  به صورت  #MyText نشان داده می شود که # مشخص میکند که منبع مورد نظر هنوز تعریف نشده است. قدم بعدی ایجاد فایل  RESX و تعریف منبع است. فرض کنید پروژه ای با نام MyApp دارید ،آنگاه می توانید به سادگی فایل  RESX با نام  TestWindow.resx ایجاد کنید و عمل ساخت آن را به Embedded Resouce  تنظیم کنید .یک رشته منبع با نام MyText اضافه کرده و پروزه را مجدد کامپایل کنید.  اگر TestWindow  را بازکنید، رشته منبع را که در TextBlock نشان داده شده است خواهید دید.

برای اضافه کردن بومی سازی ، فایل RESX را کپی کرده و نام آن را تغییر دهید . برای مثال برای بومی سازی فارسی، فایل RESX را در TestWindow.fr.resx کپی کرده و آن را به عنوان یک منبع  RESX تعبیه شده در پروژه استفاده کنید.

تنظیم کردن خاصیت  ResxNameبرای عنصر Resx در یک پنجره منجر به تعداد زیادی از کپی  XAML می شود. استفاده از خواص متصل بسیار  XAML  را مختصر میکند. و این اجازه را می دهد که خواص  ResxExtention.DefaultResxName  پیوست شده را در بسیاری از عناصر به صورت زیر تنظیم کنید.

<Window ResxExtension.ResxName="WpfApp.MainWindow" Language="{UICulture}>" 

 

اکنون خاصیت  ResxName  می تواند از عناصر  Resx  حذف شود.  اگر فقط بخواهید خاصیت  Key  را مشخص کنید ، می توانید نام پارامتر را حذف کنید تا اعلان مختصری داشته باشید.  

<TextBlock Text="{Resx MyText}"/>

تنظیمات  DefaultResxName با استفاده از ویژگی های ضمیمه شده کارمیکنند که با استفاده  از عنصر  Resx ویژگی  FrameworkElement را فراهم میکنند. هرچند عنصر  Resx در MarkupExtension دیگر کار نخواهد کرد. در این مورد نیاز است که ResxName  تعریف شود.

تغییر Culture(فرهنگ) در زمان طراحی

پنجره TestWindow.xaml  را باز کنید. باید توجه داشته باشید که در پنجره اصلی دو آیکون برای نمایش وجود خواهد داشت. که به شما امکان انتخاب  Culture های استفاده شده  در زمان طراحی را می دهد.   بر روی Icon کلیک کرده و Other Culture  را انتخاب و از لیست باز شده گزینه مورد نظر خود را انتخاب کنید. مترجم در  designer  نمایش داده می شود که به فایل  Resx مربوط به آن تغییر خواهد کرد.  

در Visual Studio 2012 , 2013  یک   designer  جدید با نام  XDesProc معرفی شد.  همانند روند طراحی خارجی توسط  Expression Blend استفاده می شود. shadow  XDesProc اسمبلی های شما را درون  دایرکتوری خود کپی میکند اما اسمبلی منبع ماهواره آن را نمی تواند کپی کند .به این معنی است که انتخاب  Culture  زمان طراحی با شکست مواجه می شود. برای حل این مشکل  راه حلی پیاده سازی کرده ایم که سازنده کلاس  ResxExtension ، رویداد  AssemblyResolve را گرفته و به دنبال آخرین اسمبلی منطبق می گردد. برای تعیین دایرکتوری ها برای جستجو ، آن را  از طریق فرایند های در حال اجرا برای فرایند میزبانی ویژوال استودیو خواهد یافت. به این معناست که اگر از designer  ویژوال استودیو استفاده میکنیدو گزینه Enable the Visual Studio hosting process  را در Debug setting   انتخاب کرده باشید ، سوئیچ کردن  culture  زمان طراحی به درستی کار خواهد کرد.  اگر این گزینه غیرفعال باشد یا از  Expression Blend  استفاده کنید باید در   ResxExtension  جستجو برای اسمبلی ماهواره در زمان طراحی تعریف شود. برای انجام این کار یک مقدار  string  در محل زیر در رجیستری ایجاد کنید.

HKEY_CURRENT_USER\Software\ResxExtension\AssemblyPath

مقدار آن را لیست مشخصی از دایرکتوری ها برای جستجو تنظیم کنید.

تغییر Culture   در زمان اجرا به صورت پویا  

Culture  برنامه شما به راحتی می تواند به صورت پویا در زمان اجرا با استفاده از ویژگی  CultureManager.UICulture   تغییر کند. این ویژگی ،  CurrentThread.UICulture  را تنظیم میکند و به صورت خودکار همه  XAML های فعال را که از  ResxExtension استفاده میکنند بروزرسانی میکند.

 

تصاویر و آیکون ها

 ResxExtensionفقط برای کار با Text  نیست .  علاوه بر  text  می تواند برای تصاویر و آیکون ها نیز بکار رود . براحتی می توان از تصاویر و آیکون های فایل Resx در XAML استفاده کرد. برای تعریف یک آیکون برای یک پنجره ، یک منبع آیکون را به فایل  Resx  با نام  Window.Iconاضافه کنید ، سپس نشانه گذاری آن را به صورت زیر تعریف کنید :

<Window Icon="{Resx Window.Icon}"/>

 

این کار بسیار راحت تر  از تعریف آیکون در WPF است. از همین روش می توانید برای منوها و نوارابزار نیز استفاده کنید.

بومی سازی انواع ویژگی های دیگر

ResxExtension می تواند برای بومی سازی ویژگی ها از هر نوعی استفاده شود. برای نمونه خاصیت  margin از یک textblock  می تواند به صورت زیر تعریف شود.

<TextBlock 
  Margin="{Resx Key=MyMargin, DefaultValue='18,0,0,71'}"/>

 

در این مثال توجه داشته باشید که یک صفت  DefaultValue  تعریف کردیم. اگر منبعی یافت و بارگذاری نشود از این مقدار استفاده می شود. اگر DefaultValue  تعریف نکرده باشید ممکن است که  XAML آن را در designer بارگذاری نکند. منبع می تواند به عنوان یک رشته ساده ("18,0,0,71")  تعریف شود ، یا می توانید آن را در فایل  Resx با یک مقدار کامل تعریف کنید :

<data name="MyMargin" type="System.Windows.Thickness, PresentationFramework">
   <value>18, 0, 0, 71</value>
</data>

فرمت اتصال داده ها

WPF عنصر Binding برای اتصال ویژگی ها به یک Datasourec  را فراهم میکند . ویژگی  Binding.StringFormat امکان فراهم کردن یک رشته برای استفاده در فرمت داده های نمایشی را  ارائه می دهد:


<Binding StringFormat="Selected Item: {0}" ElementName="_fileListBox" Path="SelectedItem"/>

 

برای بومی سازی آن باید  StringFormat خاصی را با استفاده از عنصر Resx تعریف کنیم. با استفاده از آن  Culture  زمان اجرا تغییر نمیکند. اگر  culture را تغییر دهید با  خطای  InvalidOperation با پیام" اتصال نمی تواند بعد از استفاده شدن تغییر کند" مواجه خواهید شد. متاسفانه اتصال با بروزرسانی پویا از Culture طراحی نشده است . برای غلبه به این ،  ResxExtension می تواند مانند یک اتصال عمل کند. براحتی می توانید ویژگی های اتصال را تنظیم کنید و مقدار منابع به عنوان  format stribg  استفاده شده است. برای مثال اتصال بالا را می توان به صورت زیر نوشت :

<Resx Key="MyFormatString" BindingElementName="_fileListBox" BindingPath="SelectedItem"/>

همچنین می توان یک منبع متفاوت را مشخص کرد که با استفاده از ویژگی BindingTargetNullKey ، هنگام   Nullبودن  Target از آن استفاده شود.

ResxExtension ، قالب داده را از منابع داده ای متعدد با استفاده از عناصر تو در تو Resx   پشتیبانی میکند.



<Resx Key="MyMultiFormatString">
    <Resx BindingElementName="_fileListBox" BindingPath="Name"/>
    <Resx BindingElementName="_fileListBox" BindingPath="SelectedItem"/>
</Resx> 

در این نمونه باید منبع  MyMultiFormatString با Palaceholder  برای هر دو آرگومان datasource   تعریف شود : "Selected {0}: {1}"

 

گسترش  UICulture

در این پروژه افزونه نشانه گذاری دیگری نیز با نام  UICulture تعریف شده است . که برای تنظیم زبان های یک پنجره  WPF تنظیم می شود برای تطبیق زبان با Culture  مورد نظر در برنامه (CultureManager.UICulture).  همانند ResxExtension، افزونه UICulture  نیز  در هنگام تغییر  CultureManager.UICulture  ، عناصر پیوست شده را به صورت خودکار بروزرسانی میکند.

 

برای تبدیل  Enum  ها ، کلاس  ResourceEnumConverter  به پروژه اضافه شده است. این کلاس یک مکانیزم آسان را برای بومی سازی متن های نمایشی که با   Enum  مرتبط هستند فراهم میکند .

پنهان کردن پنجره فایل های Resx  در  solutionexplorer

یکی از امکانات جالب Windows Forms Localization آن است که فایلهای Resx  مرتبط با یک فرم یا کنترل  به صورت اتوماتیک پنهان هستند. برای دیدن این فایلهای  Resx بر روی دکمه گسترش فرم یا کنترل کلیک میکنید. به این معناست که اگر زبان های بیشتری به پروژه خود اضافه کنید ، Solution explorer بهم ریخته نمی شود. همچنین می توانید این کار را برای فایلهای Resx  که با فایلهای XAML در ارتباط هستند با استفاده از  DependentUpon انجام دهید .

<EmbeddedResource Include="TestWindow.resx">
  <DependentUpon>TestWindow.xaml</DependentUpon>
  <SubType>Designer</SubType>
</EmbeddedResource>

مدیریت طول عمر شیء

مدیریت طول عمر شیء یکی از مسائل اصلی در هنگام طراحی یک MarkupExtension  است که XAML که از آن استفاده میکند  به صورت خودکار بروزرسانی خواهد شد. ResxExtension نیاز دارد که یک منبع را برای عناصر  XAML هدف نگهداری کند  که از آن برای  فعال کردن بروزرسانی عناصر هنگام تغییر  CultureManager.UICulture استفاده کند. هرچند این منبع یک منبع قوی است ، عناصر  WPF  که از extension استفاده کرده اند هرگز جمع آوری نخواهند شد. برای جلوگیری از این وضعیت ، extension  یک منبع ضعیف از عناصر هدف  WPF  را نگهداری میکند. که عناصر هدف را برای جمع آوری فعال میکند. تنها مشکل ، وجود منبع قوی برای گسترش اشیاء است. این موضوع توسط دوره فراخوانی یک تابع پاکسازی که  Extension  هایی را که اهداف فعالی ندارند برطرف می شود.

این  مدیریت طول عمر در بعضی کلاس های پایه ای پیاده سازی شده است و برای  هر  MarkupExtension که آن رفتار را نیاز داشته باشد استفاده می شود.   ManagedMarkupExtension  یک کلاس پایه ای برای  Extension ها فراهم میکند و یک منبع ضعیف برای اشیاء هدف  WPF پیاده سازی میکند.  کلاس   MarkupExtensionManager  مجموعه ای از اشیاء ManagedMarkupExtension   ها را مدیریت میکند و مکانیزم آپدیت و پاکسازی را پیاده سازی میکند.   ResxExtension  و  UICultureExtensionManager  هر دو از ManagedMarkupExtension  مشتق شده اند و از یک نمونه استاتیک کلاس MarkupExtensionManager  برای رسیدگی به آپدیت اهداف  WPF  هنگام تغییر CultureManager.UICulture استفاده میکنند.

پشتیبانی اتصال داده

غلبه بر تغییر ناپذیری اتصال داده ها کاری سخت و خسته کننده است. راه حلی کاملا ظریف وجود دارد که امکان تنظیم کردن مستقیم ویژگی های اتصال بر روی عنصر  Resx  را به ما می دهد.  ResxExtension   این ویژگیها را به یک نمونه اتصال اساسی نسبت می دهد و  Binding.StringFormat  را با مقدار منبع تنظیم میکند. زمانی که Culture  تغییر میکند ،  ResxExtension  یک کپی از اتصال ایجاد میکند و هدف را برای استفاده اتصال جدید آپدیت میکند.

پشتیبانی  Globalizer.NET

ResxExtension برای پشتیبانی بومی سازی برنامه های  WPF با استفاده از ابزار  Globalizer.NET طراحی شد. کلاس  ResxExtension شامل یک رویداد  static GetResourceمی شود که امکان قرار دادن  Globalizer.NET را درون مکانیزم ترجمه منابع به ما می دهد و به صورت پویا ترجمه را برای منابع فراهم میکند. این امکان Globalizer  را قادر می سازد ترجمه پنجره ها و کنترل هایی که از ResxExtension بدون داشتن منابع ماهواره ای استفاده میکنند را نمایش دهد .  Globalizer.NET شامل قابلیتی برای اسکن  پروژه  WPF موجود برای تعیین زبان و به طور خودکار تبدیل آنها به یکدیگر با استفاده از  ResxExtension ها است.

فایل های ضمیمه

برنامه نویسان

نویسنده 3355 مقاله در برنامه نویسان
  • WPF
  • 2k بازدید
  • 1 تشکر

کاربرانی که از نویسنده این مقاله تشکر کرده اند

در صورتی که در رابطه با این مقاله سوالی دارید، در تاپیک های انجمن مطرح کنید