官网看到关于Forms应用程序国际化的相关内容,便一边练习一边总结一下,虽然这部分知识点很可能永远用不到!!!
原文地址:https://developer.xamarin.com/zh-cn/guides/xamarin-forms/advanced/localization/
Xamarin.Forms PCL 国际化
Forms PCL中的国际化借助资源文件(RESX)实现。
准备资源文件
在可移植项目中添加资源文件:
会在项目中生成如下两个文件:
同时添加Resources.en.resx
文件,提供英文环境下使用的字符串。记得修改文件的生成操作为EmbeddedResource
。
Xamarin Studio添加名称为Resources.en资源文件时提示Resources文件已经存在,此时可以先添加名称为Resources1的资源文件,然后修改Resources1.resx和Resources1.Designer.resx文件的名称为Resources.en.resx和Resources.en.Designer.resx
Designer.cs文件不做修改,编辑.resx文件。
Resources.resx 添加如下data元素:
首页
home page title
Resources.en.resx 添加如下data元素:
Home
home page title
生成项目Designer.cs文件生成如下代码:
为了获取name为home对应的值需要调用如下代码
Resource.Resources.ResourceManager.GetString("home")
其中Resource
为resx文件所在文件夹,Resources
为根据文件名生成的类。
简化资源文件中字符串资源的获取,resx文件右键—>属性。
修改自定义工具对应的属性值:ResXFileCodeGenerator
修改为PublicResXFileCodeGenerator
。
ResXFileCodeGenerator,PublicResXFileCodeGenerator,他们的区别是前者生成的Internal的,后者生成的是Public的资源。
重新生成项目,此时可以通过如下代码Resource.Resources.home
获取home对应的值。
确定当前运行平台的语言环境
Forms本身并没有提供获取运行平台语言环境的功能,我们需要借助dependency service 实现。
在可移植项目中定义如下接口:
///
/// Implementations of this interface MUST convert iOS and Android
/// platform-specific locales to a value supported in .NET because
/// ONLY valid .NET cultures can have their RESX resources loaded and used.
///
///
/// Lists of valid .NET cultures can be found here:
/// http://www.localeplanet.com/dotnet/
/// http://www.csharp-examples.net/culture-names/
/// You should always test all the locales implemented in your application.
///
public interface ILocalize
{
///
/// This method must evaluate platform-specific locale settings
/// and convert them (when necessary) to a valid .NET locale.
///
CultureInfo GetCurrentCultureInfo();
///
/// CurrentCulture and CurrentUICulture must be set in the platform project,
/// because the Thread object can't be accessed in a PCL.
///
void SetLocale(CultureInfo ci);
}
同时定义帮助类PlatformCulture
:
///
/// Helper class for splitting locales like
/// iOS: ms_MY, gsw_CH
/// Android: in-ID
/// into parts so we can create a .NET culture (or fallback culture)
///
public class PlatformCulture
{
public PlatformCulture(string platformCultureString)
{
if (String.IsNullOrEmpty(platformCultureString))
throw new ArgumentException("Expected culture identifier", "platformCultureString"); // in C# 6 use nameof(platformCultureString)
PlatformString = platformCultureString.Replace("_", "-"); // .NET expects dash, not underscore
var dashIndex = PlatformString.IndexOf("-", StringComparison.Ordinal);
if (dashIndex > 0)
{
var parts = PlatformString.Split('-');
LanguageCode = parts[0];
LocaleCode = parts[1];
}
else
{
LanguageCode = PlatformString;
LocaleCode = "";
}
}
public string PlatformString { get; private set; }
public string LanguageCode { get; private set; }
public string LocaleCode { get; private set; }
public override string ToString()
{
return PlatformString;
}
}
在App类中构造方法中添加如下代码,如果当前运行系统为ios或android需要获取当前系统对应.net表示的语言环境并设置为当前的语言环境:
if (Device.RuntimePlatform == Device.iOS || Device.RuntimePlatform == Device.Android)
{
var localize = DependencyService.Get();
var ci = localize.GetCurrentCultureInfo();
//Resource.Resources.Culture = ci;// set the RESX for resource localization
localize.SetLocale(ci); // set the Thread for locale-aware methods
}
ios下 ILocalize 实现代码
[assembly: Xamarin.Forms.Dependency(typeof(demo.iOS.Localize))]
namespace demo.iOS
{
public class Localize : ILocalize
{
public void SetLocale(CultureInfo ci)
{
Thread.CurrentThread.CurrentCulture = ci;
Thread.CurrentThread.CurrentUICulture = ci;
Console.WriteLine("CurrentCulture set: " + ci.Name);
}
public CultureInfo GetCurrentCultureInfo()
{
var netLanguage = "en";
if (NSLocale.PreferredLanguages.Length > 0)
{
var pref = NSLocale.PreferredLanguages[0];
netLanguage = iOSToDotnetLanguage(pref);
}
// this gets called a lot - try/catch can be expensive so consider caching or something
System.Globalization.CultureInfo ci = null;
try
{
ci = new System.Globalization.CultureInfo(netLanguage);
}
catch (CultureNotFoundException e1)
{
// iOS locale not valid .NET culture (eg. "en-ES" : English in Spain)
// fallback to first characters, in this case "en"
try
{
var fallback = ToDotnetFallbackLanguage(new PlatformCulture(netLanguage));
Console.WriteLine(netLanguage + " failed, trying " + fallback + " (" + e1.Message + ")");
ci = new System.Globalization.CultureInfo(fallback);
}
catch (CultureNotFoundException e2)
{
// iOS language not valid .NET culture, falling back to English
Console.WriteLine(netLanguage + " couldn't be set, using 'en' (" + e2.Message + ")");
ci = new System.Globalization.CultureInfo("en");
}
}
return ci;
}
string iOSToDotnetLanguage(string iOSLanguage)
{
Console.WriteLine("iOS Language:" + iOSLanguage);
var netLanguage = iOSLanguage;
//certain languages need to be converted to CultureInfo equivalent
switch (iOSLanguage)
{
case "ms-MY": // "Malaysian (Malaysia)" not supported .NET culture
case "ms-SG": // "Malaysian (Singapore)" not supported .NET culture
netLanguage = "ms"; // closest supported
break;
case "gsw-CH": // "Schwiizertüütsch (Swiss German)" not supported .NET culture
netLanguage = "de-CH"; // closest supported
break;
// add more application-specific cases here (if required)
// ONLY use cultures that have been tested and known to work
}
Console.WriteLine(".NET Language/Locale:" + netLanguage);
return netLanguage;
}
string ToDotnetFallbackLanguage(PlatformCulture platCulture)
{
Console.WriteLine(".NET Fallback Language:" + platCulture.LanguageCode);
var netLanguage = platCulture.LanguageCode; // use the first part of the identifier (two chars, usually);
switch (platCulture.LanguageCode)
{
//
case "pt":
netLanguage = "pt-PT"; // fallback to Portuguese (Portugal)
break;
case "gsw":
netLanguage = "de-CH"; // equivalent to German (Switzerland) for this app
break;
// add more application-specific cases here (if required)
// ONLY use cultures that have been tested and known to work
}
Console.WriteLine(".NET Fallback Language/Locale:" + netLanguage + " (application-specific)");
return netLanguage;
}
}
}
Android下 ILocalize 实现代码
[assembly: Xamarin.Forms.Dependency(typeof(demo.Droid.Localize))]
namespace demo.Droid
{
public class Localize : ILocalize
{
public void SetLocale(CultureInfo ci)
{
Thread.CurrentThread.CurrentCulture = ci;
Thread.CurrentThread.CurrentUICulture = ci;
Console.WriteLine("CurrentCulture set: " + ci.Name);
}
public CultureInfo GetCurrentCultureInfo()
{
var netLanguage = "en";
var androidLocale = Java.Util.Locale.Default;
netLanguage = AndroidToDotnetLanguage(androidLocale.ToString().Replace("_", "-"));
// this gets called a lot - try/catch can be expensive so consider caching or something
System.Globalization.CultureInfo ci = null;
try
{
ci = new System.Globalization.CultureInfo(netLanguage);
}
catch (CultureNotFoundException e1)
{
// iOS locale not valid .NET culture (eg. "en-ES" : English in Spain)
// fallback to first characters, in this case "en"
try
{
var fallback = ToDotnetFallbackLanguage(new PlatformCulture(netLanguage));
Console.WriteLine(netLanguage + " failed, trying " + fallback + " (" + e1.Message + ")");
ci = new System.Globalization.CultureInfo(fallback);
}
catch (CultureNotFoundException e2)
{
// iOS language not valid .NET culture, falling back to English
Console.WriteLine(netLanguage + " couldn't be set, using 'en' (" + e2.Message + ")");
ci = new System.Globalization.CultureInfo("en");
}
}
return ci;
}
string AndroidToDotnetLanguage(string androidLanguage)
{
Console.WriteLine("Android Language:" + androidLanguage);
var netLanguage = androidLanguage;
//certain languages need to be converted to CultureInfo equivalent
switch (androidLanguage)
{
case "ms-BN": // "Malaysian (Brunei)" not supported .NET culture
case "ms-MY": // "Malaysian (Malaysia)" not supported .NET culture
case "ms-SG": // "Malaysian (Singapore)" not supported .NET culture
netLanguage = "ms"; // closest supported
break;
case "in-ID": // "Indonesian (Indonesia)" has different code in .NET
netLanguage = "id-ID"; // correct code for .NET
break;
case "gsw-CH": // "Schwiizertüütsch (Swiss German)" not supported .NET culture
netLanguage = "de-CH"; // closest supported
break;
// add more application-specific cases here (if required)
// ONLY use cultures that have been tested and known to work
}
Console.WriteLine(".NET Language/Locale:" + netLanguage);
return netLanguage;
}
string ToDotnetFallbackLanguage(PlatformCulture platCulture)
{
Console.WriteLine(".NET Fallback Language:" + platCulture.LanguageCode);
var netLanguage = platCulture.LanguageCode; // use the first part of the identifier (two chars, usually);
switch (platCulture.LanguageCode)
{
case "gsw":
netLanguage = "de-CH"; // equivalent to German (Switzerland) for this app
break;
// add more application-specific cases here (if required)
// ONLY use cultures that have been tested and known to work
}
Console.WriteLine(".NET Fallback Language/Locale:" + netLanguage + " (application-specific)");
return netLanguage;
}
}
}
代码设置Page的Title属性Title = Resource.Resources.home;
。
更改系统语言环境会有如下显示效果:
Doesn't work in DEBUG mode (Android only)
If the translated strings are working in your RELEASE Android builds but not while debugging, right-click on the Android Project and select Options > Build > Android Build and ensure that the Fast assembly deployment is NOT ticked. This option causes problems with loading resources and should not be used if you are testing localized apps.
XAML中实现国际化
很多时候我们会直接在Xaml中设置文本值的显示,如:。但是Xaml并没有提供翻译功能,需要我们通过自定义扩展标记(markup extension)实现。
[ContentProperty("Text")]
public class TranslateExtension : IMarkupExtension
{
readonly CultureInfo ci;
//指定为项目中默认资源文件ID
const string ResourceId = "demo.Resource.Resources";
public TranslateExtension()
{
if (Device.RuntimePlatform == Device.iOS || Device.RuntimePlatform == Device.Android)
{
ci = DependencyService.Get().GetCurrentCultureInfo();
}
}
public string Text { get; set; }
public object ProvideValue(IServiceProvider serviceProvider)
{
if (Text == null)
return "";
ResourceManager resmgr = new ResourceManager(ResourceId, GetType().GetTypeInfo().Assembly);
var translation = resmgr.GetString(Text, ci);
if (translation == null)
{
#if DEBUG
throw new ArgumentException(
String.Format("Key '{0}' was not found in resources '{1}' for culture '{2}'.", Text, ResourceId, ci.Name), "Text");
#else
translation = Text; // HACK: returns the key, which GETS DISPLAYED TO THE USER
#endif
}
return translation;
}
}
Xaml中通过如下代码设置标题:
xmlns:local="clr-namespace:demo" Title="{local:Translate home}"
中英文显示效果:
平台元素国际化
如应用程序的名称、图标和启动屏中涉及的元素国际化时需要在单独的平台项目中设置,不需要编写代码故不在记录。
参考:
https://developer.xamarin.com/zh-cn/guides/xamarin-forms/advanced/localization/#Localizing_Platform-Specific_Elements
http://blog.csdn.net/watermelon_/article/details/53418615
http://blog.csdn.net/chenliguan/article/details/50678678
项目地址:https://github.com/MyueX/xamarin.forms_news_demo