如何在.NET中进行极端的品牌/国际化

我们正计划一个非常大的应用程序。

– 我们希望将我们的申请国际化为30个国家。

– 在大多数国家/地区,可提供1到6种不同的品牌。

– 像’de’这样的某个区域设置和像’XXX’这样的品牌的每个组合可能会多次出现,因此我们需要另一个标识符来获取独特的内容:

"locale_brand_siteorigin" 

因此我们有.resx文件,如:

配置。 de.burgerking.px10 .resx

粗体打印是唯一标识符。

在运行时我们创建一个:

 var rm = new ResourceManager("MyNamespace.Configurations.UniqueIdentifier",Assembly.GetExecuting()); 

根据我们的业务逻辑,我们可以创建上面的resourceManager。

最后,我们将最终拥有180多个resx文件,其中包含唯一标识符的所有组合。

你知道更好的方法来做这种品牌吗?

4年前有人问过这个问题,但没有人回答:

实施应用程序品牌的行业标准?

UPDATE

我还想扩展我的问题,要求提供一个解决方案,展示使用cultureandregioninfobuilder类创建那些许多自定义文化的好处。

https://msdn.microsoft.com/en-us/library/system.globalization.cultureandregioninfobuilder(v=vs.110).aspx

我不建议将.resx文件用于这么大的项目。 当一个网站被翻译成许多不同的语言时,通常会有很多人参与复制管理,翻译等。这些人将无法编辑.resx文件,因为它们在技术上嵌入在应用程序代码中。 这意味着每次发生变化时,您的开发人员都必须不断更新资源……这对每个人来说都是一场真正的噩梦。

我最近为SumoSoft.Cms构建了一个数据库驱动的系统。 所有字符串都可以通过Admin面板进行管理,而在您刚刚使用的代码中:

 @CmsMethods.StringContent("ContentSection_Name", "Fallback_Value") 

此Helper查询数据库,查找类型为“ContentSection”的实体,其结构或多或少如下:

 public class ContentSection { public string Name { get; set; } public ICollection LocalizedStrings { get; set; } } 

每个LocalizedString都包含对特定Country和属性“Content”的引用,因此Helper所做的就是选择与Thread的Current Culture相匹配的那个。

扩展@ FrancescoLorenzetti84答案,我过去做过的一种方法就是将数据库检索包装在ResourceString类中,以便您可以执行以下操作:

 private static readonly ResourceString res = "The value"; 

然后在代码中引用它。 在场景后面,ResourceString类完成了工作。 这是一个例子:

 namespace ResString { public interface IResourceResolver { string Resolve(string key, string defaultValue); } public class ResourceString { public ResourceString(string value) { this.defaultValue = value; GetOwner(); } public string Value { get { if (!resolved) Resolve(); return value; } } public override string ToString() { return Value; } public static implicit operator string(ResourceString rhs) { return rhs.Value; } public static implicit operator ResourceString(string rhs) { return new ResourceString(rhs); } protected virtual void Resolve() { if (Resolver != null) { if (key == null) key = GetKey(); value = Resolver.Resolve(key, defaultValue); } else { value = defaultValue; } resolved = true; } [MethodImpl(MethodImplOptions.NoInlining)] protected virtual void GetOwner() { StackTrace trace = new StackTrace(); StackFrame frame = null; int i = 1; while (i < trace.FrameCount && (owner == null || typeof(ResourceString).IsAssignableFrom(owner))) { frame = trace.GetFrame(i); MethodBase meth = frame.GetMethod(); owner = meth.DeclaringType; i++; } } protected virtual string GetKey() { string result = owner.FullName; FieldInfo field = owner.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static).Where(f => typeof(ResourceString).IsAssignableFrom(f.FieldType) && f.GetValue(null) == this ).FirstOrDefault(); if (field != null) result += "." + field.Name; return result; } public static IResourceResolver Resolver { get; set; } private string defaultValue; private string value; private bool resolved; private string key; private Type owner; } } 

一个示例程序:

 namespace ResString { class Program { ///  /// Description for the first resource. ///  private static readonly ResourceString firstRes = "First"; ///  /// Description for the second resource. ///  private static readonly ResourceString secondRes = "Second"; ///  /// Description for the format string. ///  private static readonly ResourceString format = "{0} {1}"; static void Main(string[] args) { ResourceString.Resolver = new French(); Console.WriteLine(String.Format(format, firstRes, secondRes)); } private class French : IResourceResolver { public string Resolve(string key, string defaultValue) { switch (key) { case "ResString.Program.firstRes": return "Premier"; case "ResString.Program.secondRes": return "Deuxième"; case "ResString.Program.format": return "{1} {0}"; } return defaultValue; } } } } 

如果你运行它,它将输出:DeuxièmePremier

注释解析器分配,你会得到:第一秒

任何在UI中使用字符串的地方,都要使用声明的ResourceString。

解析字符串值后更改解析程序不会更改其值,因为只检索一次值。 您当然需要编写一个从数据库中提取的真正的解析器。

您将需要的是一个实用程序,用于运行已编译的类并提取ResourceString声明,并将键和默认值放入数据库或文本文件中,以便可以对它们进行转换。 这也应该通过为每个程序集生成的帮助XML文件,并拉出ResourceString声明的注释,以便翻译器有一些上下文可以使用。 关键声明还将提供上下文,因为您可以轻松地按UI类对资源进行分组。

将其添加到构建脚本中,确保定期更新。

您可以对图像等使用相同的方法。