6

我们正在计划一个相当大的应用程序。

-我们希望将我们的应用程序国际化到 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年前有人问过这个问题,但没有人回答:

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

更新

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

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

4

2 回答 2

2

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

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

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

此帮助程序查询数据库以查找类型为“ContentSection”的实体,其结构或多或少如下所示:

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

    public ICollection<ContentSectionLocalizedString> LocalizedStrings { get; set; }
}

每个 LocalizedString 都包含对特定 Country 的引用和属性“Content”,因此 Helper 所做的只是选择与当前线程文化匹配的那个。

于 2016-04-13T20:10:51.637 回答
1

扩展@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
    {
        /// <summary>
        /// Description for the first resource.
        /// </summary>
        private static readonly ResourceString firstRes = "First";
        /// <summary>
        /// Description for the second resource.
        /// </summary>
        private static readonly ResourceString secondRes = "Second";
        /// <summary>
        /// Description for the format string.
        /// </summary>
        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ème Premier

注释掉 Resolver assignment,你会得到: First Second

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

在解析字符串值后更改解析器不会改变它们的值,因为这些值只被检索一次。您当然需要编写一个从数据库中提取的真正解析器。

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

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

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

于 2016-04-22T00:43:34.420 回答