创建支持多种语言的解决方案。

 

发布日期: 2017年1月

适用于: Dynamics 365 (online),Dynamics 365 (on-premises),Dynamics CRM 2016,Dynamics CRM Online

Microsoft Dynamics 365(在线或本地) 支持多种语言。 如果您希望为包含不同基本语言的组织或设置多种语言的组织安装解决方案,在计划解决方案时,请将其考虑在内。 下表列出与要在支持多种语言的解决方案中包括的解决方案组件一同使用的策略。

策略

解决方案组件类型

开发人员选项

Web 资源

嵌入的标签

应用程序导航 (SiteMap)
功能区

导出和导入翻译

属性
图表
仪表板
实体
实体关系
表单
消息
选项集
视图

基本语言字符串中的本地化

合同模板
连接角色
流程(工作流)
安全角色
字段安全性配置文件

不需要本地化

SDK 消息处理步骤
服务端点

每种语言的单独组件

文章模板
电子邮件模板
邮件合并模板
报表
对话

使用 XML Web 资源作为语言资源

插件程序集

以下各节提供每个策略的其他详细信息。

开发人员选项

Web 资源具有 LanguageCode 属性,可以在用户界面中设置此属性,但是应用程序不使用此值。 由于 Web 资源是可寻址 URL,通常按名称访问 Web 资源,而不是通过将 LanguageCode 作为条件查询可用 Web 资源来访问 Web 资源。 因此,LanguageCode 是受限值。 更常见的情况是您必须基于显示 Web 资源的上下文来检测相应的语言。 对于 Web 资源,上下文对象提供对 getOrgLcidgetUserLcid 函数的访问权限。 这两个函数返回与区域设置 ID (LCID) 值对应的整数值。可在区域设置 ID (LCID) 图表中找到有效区域设置 ID 值。

对于在用户界面中公开文本的 Web 资源,您可以使用任何所需方法来管理文本和布局支持用户语言首选项的方式。 具体的实现取决于 Web 资源类型。

HTML Web 资源

一个选项是创建根据应用的名称而异的单独本地化 Web 资源。 例如,您可以拥有一个支持英语的名为 new_/my_solution/1033/content.htm 的 Web 资源和一个支持日语的名为 new_/my_solution/1041/content.htm 的 Web 资源。 有关需要从右到左方向的语言,请参阅如何:使用 HTML 标记显示从右到左的全球化文本

如果希望能够基于用户语言动态设置 UI 文本,您可以将所有本地化字符串值存储在整个脚本 Web 资源文件中定义的对象中。 根据用户的语言首选项,当页面加载时,可以使用对象中存储的字符串设置 UI 文本元素。 以下 JavaScript 代码示例演示一种设置本地化字符串的方法。

var userLcid = 1033;

var localizedStrings = {
 ErrorMessage: {
  _1033: "There was an error completing this action. Please try again.",
  _1041: "このアクションを完了、エラーが発生しました。もう一度実行してください。",
  _1031: "Es ist ein Fehler aufgetreten, der Abschluss dieser Aktion. Bitte versuchen Sie es erneut.",
  _1036: "Il y avait une erreur complétant cette action. Veuillez essayer à nouveau.",
  _1034: "Hubo un error al completar esta acción. Vuelva a intentarlo.",
  _1049: "Произошла ошибка, выполнение этого действия. Пожалуйста, попробуйте снова."
 },
 Welcome: {
  _1033: "Welcome",
  _1041: "ようこそ",
  _1031: "Willkommen",
  _1036: "Bienvenue",
  _1034: "Bienvenido",
  _1049: "Добро пожаловать"
 }
};
var LocalizedErrorMessage = localizedStrings.ErrorMessage["_" + userLcid];
var LocalizedWelcomeMessage = localizedStrings.Welcome["_" + userLcid];

silverlight web 资源

可以编写 Silverlight 应用程序以支持本地化语言资源。详细信息:本地化基于 Silverlight 的应用程序

下面的类提供对基于 Silverlight Web 资源显示上下文的用户语言首选项的访问权限。 此类支持美国英语、阿拉伯语、德语、希伯来语和日语。 对于 Silverlight Web 资源支持的特定语言,需要修改它。

public static class Localization    
    {
    // The locale ID.
    public static int LCID { get; set; }
    // Create a dictionary of right-to-left language codes (Hebrew and Arabic).
    private static Dictionary<int, bool> _rightToLeftLanguages =
        new Dictionary<int, bool>
        {
            { 1025, true },
            { 1037, true },
        };
    private static Dictionary<int, String> _lcidToCultureNameMap =
        new Dictionary<int, String>
        {
            { 1025, "ar-SA" },
            { 1031, "de-DE" },
            { 1033, "en-US" },
            { 1037, "he-IL" },
            { 1041, "ja-JP" }
        };
    public static void InitializeCulture()
    {
        // Get the user's LCID from the page's context to determine what language to
        // display.
        dynamic window = HtmlPage.Window;
        //Get the user's language code from the context.
        try
        {
            //If the containing window is a CRM form
            LCID = Convert.ToInt32(window.Xrm.Page.context.getUserLcid());
            // For testing, comment the line above and uncomment one of the lines below
            //representing a user language.
            //LCID = 1033; //English
            //LCID = 1041; //Japanese
            //LCID = 1031; //German
            //LCID = 1037; //Hebrew - a right-to-left language.
            //LCID = 1025; //Arabic - a right-to-left language.
        }
        catch (Microsoft.CSharp.RuntimeBinder.RuntimeBinderException)
        {
            try
            {
                //If the containing window is a CRM web resource with
                //the WebResources/ClientGlobalContext.js.aspx page linked
                LCID = Convert.ToInt32(window.GetGlobalContext().getUserLcid());
            }
            catch (Microsoft.CSharp.RuntimeBinder.RuntimeBinderException)
            {
                LCID = 1033; //Setting a default for design time when the context
                //object is not present and one of the sample languages are not set.
            }
        }
        // Sets the culture of the thread to the appropriate culture, based on what
        // LCID was retrieved.
        if (_lcidToCultureNameMap.ContainsKey(LCID))
        {
            var culture = new CultureInfo(_lcidToCultureNameMap[LCID]);
            Thread.CurrentThread.CurrentCulture = culture;
            Thread.CurrentThread.CurrentUICulture = culture;
        }
    }
    public static FlowDirection GetFlowDirection()
    {
        if (_rightToLeftLanguages.ContainsKey(LCID))
        {
            return FlowDirection.RightToLeft;
        }
        return FlowDirection.LeftToRight;
    }
}

有关需要从右到左方向的语言,请参阅 FrameworkElement.FlowDirection 属性

XML Web 资源

虽然 XML Web 资源通常不显示给用户,但它们可用于将本地化字符串存储为 使用 XML Web 资源作为语言资源 中所述的其他解决方案组件资源。

嵌入的标签

使用此策略的每个解决方案组件要求所有本地化文本包括在解决方案组件中。

功能区

当安装语言包时,应用程序功能区自动显示功能区中所有默认文本的本地化文本。 系统标签是在仅供内部使用的 ResourceId 属性值中定义的。 当添加自己的文本时,应使用 <LocLabels> (RibbonDiffXml) 元素提供您支持的语言的本地化文本。详细信息:对功能区使用本地化标签

SiteMap

当安装语言包时,应用程序导航栏中的默认文本自动显示本地化文本。 若要替代默认文本或提供您自己的文本,请使用 <Titles> (SiteMap) 元素。Titles 元素应包含 <Title> (SiteMap) 元素,该元素包含您的解决方案支持的所有语言的本地化文本。 如果 Title 元素不可用于用户的首选语言,则显示与组织基本语言对应的标题。

<SubArea> (SiteMap) 元素允许使用 userlcid 参数传递用户的语言首选项,以便作为 SubArea.Url 属性目标的内容可以了解用户的语言首选项并相应地调整。详细信息:使用站点地图将参数传递给 URL

导出和导入翻译

可以导出下表中的解决方案组件的可本地化标签,以便于本地化。

实体

属性

关系

全局选项集

实体消息

实体窗体

实体视图 (SavedQuery)

图表

仪表板​​

翻译标签和显示字符串

只能使用基础语言在应用程序中执行自定义。 因此,当要为这些自定义提供本地化标签和显示字符串时,必须导出标签的文本,以便它们可以针对为组织启用的任何其他语言本地化。 使用以下步骤:

  1. 确保您所在的组织已针对要为其提供翻译的语言安装了所有 MUI 包且设置了语言。

  2. 创建解决方案并修改组件

  3. 完成开发解决方案之后,请使用“导出翻译”功能。 这会生成包含需要翻译的所有标签的 Microsoft Office Excel 电子表格 (CrmTranslations.xml)。

  4. 在该电子表格上,提供了相应的翻译。

  5. 使用“导入翻译”功能将翻译导回到相同的 Microsoft Dynamics 365 组织,然后发布您的更改。

  6. 下次导出解决方案时,它将会支持您提供的所有翻译。

当导入某个解决方案时,将会放弃目标系统中不可用的语言标签并且记录警告。

如果解决方案包中未提供目标系统基本语言的标签,则会改为使用源系统基本语言的标签。 例如,如果您导入的解决方案包含英语和法语的标签并且以英语作为基本语言,但是目标系统具有日语和法语并且以日语作为基本语言,则将会使用英语标签而不是日语标签。 基本语言标签不能是 null 值或为空。

正在导出翻译

在导出翻译之前,必须先安装语言包并设置要本地化的所有语言。 可以在 Web 应用程序中或使用 ExportTranslationRequest 消息导出翻译。 有关详细信息,请参阅帮助和培训:导出自定义实体和字段文本以转换

正在翻译文本

当您在 Excel 中打开 CrmTranslations.xml 文件时,将显示下表中列出的三个工作表。

工作表

说明

信息

显示有关标签和字符串从中导出的组织和解决方案的信息。

显示字符串

显示代表与元数据组件关联的所有消息文本的字符串。 此表包含用于系统功能区元素的错误消息和字符串。

本地化标签

显示所有元数据组件标签的所有文本。

可将此文件发送给语言专家、翻译机构或本地化公司。 他们需要为所有空单元格提供本地化字符串。

备注

对于自定义实体,有一些与系统实体共享的常用标签,例如“创建时间”或“创建者”。 由于您已安装和设置语言,如果导出默认解决方案的语言,您能够将自定义实体中的某些标签与其他实体使用的相同标签的本地化文本相匹配。 这可以减少本地化成本和提高一致性。

本地化工作表中的文本后,将 CrmTranslations.xml 和 [Content_Types].xml 文件添加到一个压缩 .zip 文件中。 您现在可以导入此文件。

如果喜欢以编程方式将导出文件处理为 XML 文档,请参阅 Office 2003 XML 参考架构 以获取有关这些文件使用架构的信息。

正在导入已翻译的文本

重要

您只能将已翻译文本重新导入回导出它的同一组织。

在导出自定义实体或属性文本并翻译后,您可以使用 ImportTranslationRequest 消息在 Web 应用程序中导入翻译完的文本字符串。 导入的文件必须是在根目录包含 CrmTranslations.xml 和 [Content_Types].xml 文件的压缩文件。 有关详细信息,请参阅帮助和培训:导入已转换的实体和字段文本

导入已完成的翻译后,会向使用自定义文本目标翻译语言的用户显示该文本。

备注

Microsoft Dynamics 365 无法导入长度超过 500 个字符的已翻译文本。 如果翻译文件中的任何一项的长度超过 500 个字符,则导入过程失败。 如果导入过程失败,请检查文件中导致失败的行并减少该行的字符数,然后重新尝试导入。

因为只支持使用基本语言进行自定义,所以您只能在基本语言设置为语言首选项的 Microsoft Dynamics 365 中进行自定义。 若要验证已翻译的文本是否已显示,必须更改 Microsoft Dynamics 365 用户界面的语言首选项。 若要执行其他自定义工作,必须将语言首选项重新更改为基本语言。

基本语言字符串中的本地化

某些解决方案组件不支持多种语言。 这些组件包括只能在特定语言中有意义的名称或文本。 如果创建特定语言的解决方案,请为所需组织基本语言定义这些解决方案组件。

如果需要支持多种语言,一种策略是在基本语言字符串中包括本地化。 例如,如果您有一个名为“朋友”的连接角色,而您需要支持英语、西班牙语和德语,您可以使用文本““Friend (Amigo / Freund)”作为连接角色的名称。 由于文本长度的问题,对于使用此策略支持的语言数量有限制。

此组中的某些解决方案组件仅显示给管理员。 由于系统的自定义只能在组织基本语言中完成,因此需要提供多个语言版本。安全角色字段安全配置文件组件属于此组。

合同模板提供一种服务合同的说明。 它们需要“名称和“缩写”字段的文本。 您应当考虑使用对组织中的所有用户唯一且适当的名称和缩写。

连接角色依赖于选择描述性连接角色类别和名称的用户。 由于它们可能相当短,因此建议您在基本语言字符串中包括本地化。

只要解决方案库不需要将记录随本地化文本更新,为事件启动的**流程(工作流)**就可以正常工作。 可以使用工作流程序集,以便应用于本地化文本的逻辑可以使用与插件程序集相同的策略(使用 XML Web 资源作为语言资源)。

按需工作流需要一个名称,以便用户可以选择它们。 除了在按需工作流的名称中包括本地化之外,另一个策略是创建多个具有本地化名称的工作流(每个工作流调用相同的子流程)。 不过,所有用户将看到按需工作流的完整列表,而不仅是采用其首选用户界面语言的列表。

不需要本地化

SDK 消息处理步骤服务端点解决方案组件不向用户公开可本地化的文本。 如果这些组件具有对应于组织基本语言的名称和描述是非常重要的,您可以创建和导出具有该语言的名称和说明的托管解决方案。

每种语言的单独组件

以下每个解决方案组件都可能包含很大数量的要本地化的文本:

  • 文章模板

  • 电子邮件模板

  • 邮件合并模板

  • 报表

  • 对话框

对于这些解决方案组件类型,推荐的策略是为每种语言创建单独的组件。 这意味着通常应创建一个基本托管解决方案,其中包含您的核心解决方案组件,然后创建单独的托管解决方案,其中包含每种语言的这些解决方案组件。 客户安装基本解决方案之后,他们可以安装为组织设置的语言的托管解决方案。

流程(工作流)不同,您可以创建将反映用户当前语言首选项设置的对话并只将这些对话显示给使用该语言的用户。

创建本地化对话

  1. 安装适当的语言包并设置语言。

    有关详细信息,请参阅TechNet:语言包安装说明

  2. 更改个人选项以指定对话所需语言的“用户界面语言”。

  3. 导航到“设置”,在“流程中心”组中,选择“流程”。

  4. 单击“新建”,以您指定的语言创建对话。

  5. 创建完对话后,更改个人选项以指定组织基本语言。

  6. 当使用组织基本语言时,您可以导航到“设置”中的“解决方案”区域,将本地化对话作为解决方案的一部分添加进来。

以另一种语言创建的对话将只显示给使用该语言查看 Microsoft Dynamics 365 的用户。

使用 XML Web 资源作为语言资源

插件程序集解决方案组件可以通过引发 InvalidPluginExecutionException 以及创建和更新记录来向最终用户发送消息。 与 Silverlight Web 资源不同,插件无法使用资源文件。

当插件需要本地化文本时,您可以使用 XML Web 资源存储本地化字符串,因此插件可以在需要时访问它们。 XML 结构是您的选项,但是您可能想要按照 ASP.NET 资源 (.resx) 文件来为每种语言创建单独的 XML Web 资源。 例如,以下是名为 localizedString.en_US 的 XML Web 资源,后跟 .resx 文件使用的模式。

<root>
 <data name="ErrorMessage">
  <value>There was an error completing this action. Please try again.</value>
 </data>
 <data name="Welcome">
  <value>Welcome</value>
 </data>
</root>

下面的代码演示如何将本地化的消息传递回插件以将消息显示给用户。 它是 Account 实体的 Delete 事件的前期验证阶段:

protected void ExecutePreValidateAccountDelete(LocalPluginContext localContext)
  {
   if (localContext == null)
   {
    throw new ArgumentNullException("localContext");
   }
   int OrgLanguage = RetrieveOrganizationBaseLanguageCode(localContext.OrganizationService);
   int UserLanguage = RetrieveUserUILanguageCode(localContext.OrganizationService,
 localContext.PluginExecutionContext.InitiatingUserId);
   String fallBackResourceFile = "";
   switch (OrgLanguage)
   {
    case 1033:
     fallBackResourceFile = "new_localizedStrings.en_US";
     break;
    case 1041:
     fallBackResourceFile = "new_localizedStrings.ja_JP";
     break;
    case 1031:
     fallBackResourceFile = "new_localizedStrings.de_DE";
     break;
    case 1036:
     fallBackResourceFile = "new_localizedStrings.fr_FR";
     break;
    case 1034:
     fallBackResourceFile = "new_localizedStrings.es_ES";
     break;
    case 1049:
     fallBackResourceFile = "new_localizedStrings.ru_RU";
     break;
    default:
     fallBackResourceFile = "new_localizedStrings.en_US";
     break;
   }
   String ResourceFile = "";
   switch (UserLanguage)
   {
    case 1033:
     ResourceFile = "new_localizedStrings.en_US";
     break;
    case 1041:
     ResourceFile = "new_localizedStrings.ja_JP";
     break;
    case 1031:
     ResourceFile = "new_localizedStrings.de_DE";
     break;
    case 1036:
     ResourceFile = "new_localizedStrings.fr_FR";
     break;
    case 1034:
     ResourceFile = "new_localizedStrings.es_ES";
     break;
    case 1049:
     ResourceFile = "new_localizedStrings.ru_RU";
     break;
    default:
     ResourceFile = fallBackResourceFile;
     break;
   }
   XmlDocument messages = RetrieveXmlWebResourceByName(localContext, ResourceFile);
   String message = RetrieveLocalizedStringFromWebResource(localContext, messages, "ErrorMessage");
   throw new InvalidPluginExecutionException(message);
  }
  protected static int RetrieveOrganizationBaseLanguageCode(IOrganizationService service)
  {
   QueryExpression organizationEntityQuery = new QueryExpression("organization");
   organizationEntityQuery.ColumnSet.AddColumn("languagecode");
   EntityCollection organizationEntities = service.RetrieveMultiple(organizationEntityQuery);
   return (int)organizationEntities[0].Attributes["languagecode"];
  }
  protected static int RetrieveUserUILanguageCode(IOrganizationService service, Guid userId)
  {
   QueryExpression userSettingsQuery = new QueryExpression("usersettings");
   userSettingsQuery.ColumnSet.AddColumns("uilanguageid", "systemuserid");
   userSettingsQuery.Criteria.AddCondition("systemuserid", ConditionOperator.Equal, userId);
   EntityCollection userSettings = service.RetrieveMultiple(userSettingsQuery);
   if (userSettings.Entities.Count > 0)
   {
    return (int)userSettings.Entities[0]["uilanguageid"];
   }
   return 0;
  }
  protected static XmlDocument RetrieveXmlWebResourceByName(LocalPluginContext context, string webresourceSchemaName)
  {
   context.TracingService.Trace("Begin:RetrieveXmlWebResourceByName, webresourceSchemaName={0}", webresourceSchemaName);
   QueryExpression webresourceQuery = new QueryExpression("webresource");
   webresourceQuery.ColumnSet.AddColumn("content");
   webresourceQuery.Criteria.AddCondition("name", ConditionOperator.Equal, webresourceSchemaName);
   EntityCollection webresources = context.OrganizationService.RetrieveMultiple(webresourceQuery);
   context.TracingService.Trace("Webresources Returned from server. Count={0}", webresources.Entities.Count);
   if (webresources.Entities.Count > 0)
   {
    byte[] bytes = Convert.FromBase64String((string)webresources.Entities[0]["content"]);
    // The bytes would contain the ByteOrderMask. Encoding.UTF8.GetString() does not remove the BOM.
    // Stream Reader auto detects the BOM and removes it on the text
    XmlDocument document = new XmlDocument();
    document.XmlResolver = null;
    using (MemoryStream ms = new MemoryStream(bytes))
    {
     using (StreamReader sr = new StreamReader(ms))
     {
      document.Load(sr);
     }
    }
    context.TracingService.Trace("End:RetrieveXmlWebResourceByName , webresourceSchemaName={0}", webresourceSchemaName);
    return document;
   }
   else
   {
    context.TracingService.Trace("{0} Webresource missing. Reinstall the solution", webresourceSchemaName);
    throw new InvalidPluginExecutionException(String.Format("Unable to locate the web resource {0}.", webresourceSchemaName));
    return null;
 // This line never reached
   }
  }
  protected static string RetrieveLocalizedStringFromWebResource(LocalPluginContext context, XmlDocument resource, string resourceId)
  {
   XmlNode valueNode = resource.SelectSingleNode(string.Format(CultureInfo.InvariantCulture, "./root/data[@name='{0}']/value", resourceId));
   if (valueNode != null)
   {
    return valueNode.InnerText;
   }
   else
   {
    context.TracingService.Trace("No Node Found for {0} ", resourceId);
    throw new InvalidPluginExecutionException(String.Format("ResourceID {0} was not found.", resourceId));
   }
  }

另请参阅

使用解决方案打包和分发扩展
解决方案简介
解决方案开发的规划
解决方案组件的依赖项跟踪
创建、导出或导入非托管解决方案
创建、安装和更新托管解决方案
卸载或删除解决方案
解决方案实体
本地化产品属性值

Microsoft Dynamics 365

© 2017 Microsoft。 保留所有权利。 版权