Class design for internationalized object

I’m looking for some pointers on class design for a global application.

Let’s say I have to make a class structure to manage products, and the products are sold in different countries. Some of the fields for the product will have the same value across all countries (eg. product code, ERP Description) I will call these “international” fields, and some fields will be specific to a single country (eg. Local Description), lets call these “local” fields. Of course, some “local” fields will be the same for groups of countries (es. weight : 1 kilogram / 2 pounds). Also I expect that not all countries will have values for all fields.

Which fields are “international” and which fields are “local” may change from one installation to another and I am reluctant to bake this into the design as I’m sure it will bite me later on.

So, I’m trying to figure out how to structure the objects so that I can use a product at an international level and always refer to the same “product”, but also maintain and use the local information when necessary?

Just to be clear, I’m not talking about user-locale, number or date formatting etc. The source data is coming from different database schemas (one for each country). The end product will be written in C#.

I’m wondering if anyone has experience or can point me to a pattern that would provide a good solution to this before I go and reinvent the wheel?

6

I assume you have a database of some sort. I’d create a factory class that creates the objects (e.g. Product) from the data in the database.

Let’s say your designing the Product class. It has some of your international fields:

public class Product
{
    private string ean;
}

For internationalized things like weights I’d write some structs Weight that allows you to set the unit (pounds, kilograms). Use one consistent unit of weight in your database (for example kilograms) and use that to create the object. Then code your ToString to return the value localized in the specified unit. This is similar to how DateTime can take a date/time in UTC and ‘localized’ it to the user’s timezone.

When there is no weight, use for example a weight of -1 and declare this as static readonly Weight None = new Weight(-1).

public class Product
{
    private string ean;
    private Weight weight;
    private DateTime availableFrom;
}

public struct Weight
{
    private static readonly Weight None = new Weight(-1);

    private int weightInKG;

    public Weight(int weightInKG)
    {
        this.weightInKG = weightInKG;
    }

    public WeightUnit Unit
    { get; set; }

    public string ToString()
    { /* Implement */ }
}

Then, if you have pieces of text that are localized (translated) then I’d just use strings. The factory class should get the appropriate localized string from the database. If there is no such string, use null.

public class Product
{
    private string ean;
    private Weight weight;
    private DateTime availableFrom;
    private string description;
}

Lastly, if you have information that is always used together localized (for example, specs for the product) then create a class Specs (or a hierarchy SpecsBase MonitorSpecs HardDiskSpecs if there are multiple kinds) for this. When there are none, use null. You can share these objects among multiple products if the information is the same. Again, the factory should take care of creating it.

You can also use these objects for fields that might be international or local depending on the installation.

public class Product
{
    private string ean;
    private Weight weight;
    private DateTime availableFrom;
    private string description;
    private Specs specs;
}

For all objects, override the ToString method to return the right (localized) strings.

3

I’d just create a class with all the fields you need, then delegate instance creation to a factory. The factory will create instances of the class based on whatever locale criteria you discover is best (whether you specify the locale in the create method or specify a locale in the factory’s constructor). The factory will hold all the logic for assembling objects from whatever tables you have. You can have different strategies for handling missing or default values (if these vary based on locale), and which fields are “localized”. This concentrates all the logic in one place, yet gives you good flexibility.

1

Define a table for products which contains

ProductCode, ERP Description and other “constant” fields.

Define a table for properties which contains only 4 columns:

PropertyId(long),
PropertyOwnerId(long),
PropertyType(int, predefined in C# code, enumeration for instance),
PropertyValue(nvarchar, will be parsed depending on PropertyType in C# code, Object in C# code).

This is enough to bind the properties with thier owners. Later, if the properties change, all you have to do is insert new records to the second table.

The classes should have the same structure. This design is a little uncomfortable in sence that you’ll have to parse a property read from DB or cast a property in C# code, but it is extremely flexible.

P.S if you think that some of the “constant” fields are likely to change, insert them as properties into the second table.

2

In the end I used a Product class with generic content and a detail class accessed through indexers to provide the functionality… but I’m really not sure its the right way to do it at all.

I didn’t find a specific design pattern that explicitly defines how to do this (as per the original question). Except “Factory”, but that seems bit generic…

This is how I implemented it (code has been simplified):

public class Product    
{   
    public string Code {get;set;}
    ....
    private Dictionary<string, ProductDetail> LocalProduct= new Dictionary<string, ProductDetail>();

    public LocalDetail this[string countryCode]
    {
        get
        {
            if (LocalProducts.ContainsKey(countryCode))
                return LocalProducts[countryCode];
            else
                throw new ApplicationException(String.Format("Local product detail does not exist for {0}", countryCode));
         }
         set
         {
            if (LocalProducts.ContainsKey(countryCode))
                throw new ApplicationException(String.Format("Local product detail already exists for {0}", countryCode));
            else
                LocalProducts.Add(countryCode, value);
        }
    }
}

public class LocalDetail 
{
    public string Description {get; set;}
    ....
}

The calling code is something like this :

        Product p = new Product("123456");

        LocalDetail UKDetail = new LocalDetail();
        UKDetail.Description = "Description in English";
        p["UK"] = UKDetail;            

        System.Console.WriteLine("Product {0} Description in UK Detail : {1}", p.Code, p["UK"].Description);

        LocalDetail ITDetail = new LocalDetail();
        ITDetail.Description = "Descrizione in Italiano";
        p["IT"] = ITDetail;
        System.Console.WriteLine("Product {0} Description in Italian Detail : {1}", p.Code, p["IT"].Description);

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật