6

かなり大きなアプリケーションを計画しています。極端なブランディング/国際化を行う方法

-私たちは30カ国の国での出願を国際化したいと考えています。

- ほとんどの国で、1〜6種類のブランドが用意されています。

- 各「XXX」のような「デ」とブランドのような特定のロケールの組み合わせが複数回発生する場合がありますので、私たちはユニークな何かを得るために別の識別子を必要とする:

"locale_brand_siteorigin" 

をそこで私たちは似た.resxファイルを持っています:

設定。 de.burgerking.px10 .resx

太字で印刷されたものは固有の識別子です。

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

我々は上記するresourceManagerを作成することができ、当社のビジネスロジックに応じて:実行時に

私たちが作成します。

最後に、ユニークな識別子のすべての組み合わせを含む180個以上のresxファイルが作成されます。

あなたはこの種のブランドを行うためのより良い方法を知っていますか?

4年前、誰かがこの質問をしたが、どれも答えません:

Industry standard for implementing application branding?

UPDATE

私も を使用する利点を示す解決策を求めて私の質問を拡張したい

cultureandregioninfobuilderクラスを使用して、多くのカスタムカルチャを作成します。

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

+0

ほとんどの場合、独自のソリューションを実装する必要がありますが、これは簡単です。リソースID、カルチャ、ブランド(nullable)、値、デフォルト値へのフォールバック(データベースごとにローカライズする必要はありません)を持つデータベースのテーブル。もちろん、アプリケーションの起動時にメモリにキャッシュされます。そのようなものをあなたのコードベースと一緒に保存することは、とにかく良い考えではありません。 – Evk

答えて

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には特定の国とプロパティ "Content"への参照が含まれているため、Helperはすべて、現在のスレッドのCultureと一致するものを選択します。

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プレミア

コメント及び必要になりますget: First Second

UIで文字列を使用する場合は、代わりに宣言されたResourceStringを使用します。

文字列値が解決された後にリゾルバを変更しても、値が1回だけ取得されるため、値は変更されません。もちろん、データベースから取得する実際のリゾルバを書く必要があります。

コンパイルされたクラスを実行し、ResourceString宣言を引き出し、キーとデフォルト値をデータベースやテキストファイルに入れて翻訳できるようにするユーティリティプログラムが必要です。これはまた、アセンブリーごとに生成されたヘルプXMLファイルを通って、ResourceString宣言のコメントをプルする必要があります。キー宣言は、UIクラスによってリソースを簡単にグループ化できるため、コンテキストも提供します。

これをビルドスクリプトに追加して、定期的に更新されるようにしてください。

画像などで同じ方法を使用できます。