Xamarin.Forms 应用程序国际化

官网看到关于Forms应用程序国际化的相关内容,便一边练习一边总结一下,虽然这部分知识点很可能永远用不到!!!
原文地址:https://developer.xamarin.com/zh-cn/guides/xamarin-forms/advanced/localization/

Xamarin.Forms PCL 国际化

Forms PCL中的国际化借助资源文件(RESX)实现。

准备资源文件

在可移植项目中添加资源文件:

Xamarin.Forms 应用程序国际化_第1张图片
添加资源文件

会在项目中生成如下两个文件:

同时添加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文件右键—>属性。

Xamarin.Forms 应用程序国际化_第2张图片

修改自定义工具对应的属性值: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

你可能感兴趣的:(Xamarin.Forms 应用程序国际化)