EF Code First 多语言支持

7
假设您需要使用EF Code First和POCO类开发支持多语言的网站,您将如何对您的POCO类进行建模以支持此场景?
通过多语言支持,我指的不仅是在某些资源文件中为您的UI翻译标签,而且您还需要在数据库中存储给定域实体的名称、描述等。
例如:假设您的域模型具有一个实体产品。在您的数据库中,您需要为2种语言(但未来可以添加更多语言)存储产品的名称和描述(为简单起见,使用2个字段)。 根据当前文化,UI需要从资源文件中获取标签文本(这很容易实现),但您还需要显示来自数据库的产品名称和描述。
1个回答

13

最后,我已经创建了自己的类来支持数据库数据的多语言。

我是西班牙人,我已经将我的代码翻译后发布在这里,也许会有一些简单的语法错误。

  1. 你有一个名为Product的类,它有一个MultilingualString类型的ProductName属性
  2. MultilingualString类对于每种未知语言都有一个默认文本,并且有一个翻译列表。它有用于设置不同翻译(SetTranslation)和翻译到特定语言的方法。最后,该类还有一个属性用于根据当前线程文化进行翻译(Translation)
  3. 你有一个名为Translation的类,它包含语言和翻译后的文本

以下是代码:

public class Product
{
    public int Id { get; set; }
    public MultilingualString ProductName { get; set; }
}

public class MultilingualString
{
    // You need it only to make the class persistent
    public int Id { get; set; }

    public string Text { get; set; }
    public virtual ICollection<Translation> Translations { get; set; }

    public string Translation { get { return Translate(); } }

    public void SetTranslation(string language, string text)
    {
        if (Translations == null)
            Translations = new List<Translation>();

        var found = Translations.Where(t => t.Language.ToUpper() == language.ToUpper()).FirstOrDefault();
        if (found == null)
            Translations.Add(new Translation() { Language = language, Text = text });
        else
            found.Text = text;
    }

    public string Translate(string cultureName)
    {
        return Translate(new CultureInfo(cultureName));
    }


    public string Translate(CultureInfo culture = null)
    {
        if (culture == null)
            culture = Thread.CurrentThread.CurrentCulture;

        var translation = Translations == null
                             ? null
                             : Translations.Where(
                                 t =>
                                 culture.Name.ToUpper(CultureInfo.InvariantCulture).StartsWith(
                                     t.Language.ToUpper(CultureInfo.InvariantCulture))).FirstOrDefault();

        return translation == null ? Text : translation.Text;
    }
}

public class Translation
{
    // You need it only to make the class persistent
    public int Id { get; set; }
    public string Language { get; set; }
    public string Text { get; set; }
}

你可以构建DbContext类并进行如下测试:
public class MultilingualContext : DbContext
{
    public DbSet<Product> Products { get; set; }
}

[TestClass]
public class Tests
{
    [TestMethod]
    public void ThisWorksFine()
    {
        using (var context = new MultilingualContext())
        {
            var product = new Product();
            context.Products.Add(product);

            product.ProductName = new MultilingualString() { Text = "Beer (default language)" };
            product.ProductName.SetTranslation("en", "Beer");
            product.ProductName.SetTranslation("es", "Cerveza");
            product.ProductName.SetTranslation("fr", "Bière");
            product.ProductName.SetTranslation("de", "Bier");

            var spanishProductName = product.ProductName.Translate(new CultureInfo("es"));
            Assert.AreEqual(spanishProductName, "Cerveza");

            var culture = Thread.CurrentThread.CurrentCulture;

            Thread.CurrentThread.CurrentCulture = new CultureInfo("en");
            Assert.AreEqual(product.ProductName.Translation, "Beer");
            Thread.CurrentThread.CurrentCulture = new CultureInfo("es");
            Assert.AreEqual(product.ProductName.Translation, "Cerveza");
            Thread.CurrentThread.CurrentCulture = new CultureInfo("fr");
            Assert.AreEqual(product.ProductName.Translation, "Bière");
            Thread.CurrentThread.CurrentCulture = new CultureInfo("de");
            Assert.AreEqual(product.ProductName.Translation, "Bier");
            Thread.CurrentThread.CurrentCulture = new CultureInfo("it");
            Assert.AreEqual(product.ProductName.Translation, "Beer (default language)");

            Thread.CurrentThread.CurrentCulture = culture;

            context.SaveChanges();


        }
    }
}

我刚刚结束了它,希望这个能正常工作 ;)

最好的问候!

更新

还有一件事,当您删除产品时,请勿删除多语言字符串条目。您可以将业务逻辑添加到您的服务中,并在删除产品时删除多语言条目。

或者不要这样做!如果您不删除多语言条目,则会有越来越多的MultilingualStrings条目,您可以利用此存储库为未来的产品提供翻译后的产品名称。

您可以将MultilingualStrings集添加到上下文中:

public class MultilingualContext : DbContext
{
    public DbSet<Product> Products { get; set; }
    public DbSet<MultilingualString> MultilingualStrings { get; set; }
}

然后你可以在ProductNameTextBoxOnChange事件中搜索MultilingualStrings,例如:
context.MultilingualStrings.Where(s => s.Translation.StartsWith(ProductNameTextBox.Text));

你可以使用这个代码库来处理其他具有多语言字符串的实体的其他属性。


1
嗨,@javier-ros,您是否仍在使用此方法?您是如何设置翻译模型的?您是否为所有实体使用通用地图实体/表?问候。 - Rubens Mussi Cury
是的!我正在使用EF6创建的默认模型。我有一个名为“MultilingualString”的表,另一个名为“Translation”的表与1到n的关系。 “Product”表与“MultilingualString”是1到n的关系。 - Javier Ros
@javiwe-ros 但是你是如何处理所有其他表格(不仅仅是产品)的翻译的呢? 你在MultilingualString中创建了任何类型的EntityID属性以便识别它吗?! 这就是我试图找出的要点... - Rubens Mussi Cury
我觉得有点奇怪,在搜索了一个小时互联网之后,这对我来说似乎是解决某些常见设计问题的最佳方法。而且它只有9个赞。我错过了什么吗?还有其他我不知道的解决这个问题的方法吗? - dave
同意,也许用户不需要这个功能 :),我看到应用程序实现了静态内容的多语言支持,但从未实现动态内容的多语言支持。我理解设计师考虑只有一个数据源或数据库用于多语言处理。无论如何,如果您发现其他有趣的方法,请告诉我。 - Javier Ros

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接