多くの場合、ユーザーに表示するメッセージを生成するとき、メッセージには、顧客に通知したい何かがいくつか含まれています。
例を挙げましょう。顧客が1以上のアイテムをいくつか選択し、[削除]をクリックしました。次に、顧客に確認メッセージを送信します。顧客が選択したアイテムの数を示して、アイテムの束を選択し、そのうちの1つだけを削除したい場合は[削除]をクリックして、間違いを犯す可能性を最小限に抑えます。それら。
1つの方法は、次のような一般的なメッセージを作成することです。
int noofitemsselected = SomeFunction();
string message = "You have selected " + noofitemsselected + " item(s). Are you sure you want to delete it/them?";
ここでの「問題」は、noofitemselected
が1の場合であり、itemとitを記述する必要があります。 itemsおよびthemの代わりに。
私の通常の解決策は次のようになります
int noofitemsselected = SomeFunction();
string message = "You have selected " + noofitemsselected + " " + (noofitemsselected==1?"item" : "items") + ". Are you sure you want to delete " + (noofitemsselected==1?"it" : "them") + "?";
コード内に複数の数字への参照が多数ある場合、これは非常に長く、非常に厄介になり、実際のメッセージが読みにくくなります。
だから私の質問は単純です。このようなメッセージを生成するためのより良い方法はありますか?
[〜#〜]編集[〜#〜]
メッセージをメッセージボックス内に表示するように言った場合、多くの人が非常にハングアップしているのを見て、メッセージボックスをまったく使用しないようにする方法の答えを単に与えただけで、それはすべて良いことです。
ただし、複数化の問題は、メッセージボックスに加えて、プログラム内の他の場所のテキストにも当てはまることに注意してください。たとえば、グリッドで選択された行数を表示するグリッドの横のラベルには、複数化に関して同じ問題があります。
したがって、これは基本的に、プログラムから何らかの方法で出力されるほとんどのテキストに当てはまります。その場合、解決策は、プログラムを変更してテキストを出力しないようにするほど簡単ではありません:)
どんなに小さくても、このアプリを他の言語に翻訳する必要がある可能性がある場合は、両方とも間違っています。これを行う正しい方法は次のとおりです。
_string message = ( noofitemsselected==1 ?
"You have selected " + noofitemsselected + " item. Are you sure you want to delete it?":
"You have selected " + noofitemsselected + " items. Are you sure you want to delete them?"
);
_
これは、言語が異なれば複数の処理も異なるためです。マレー語のようなものは構文上の複数形さえ持っていないので、文字列は一般的に同一です。 2つの文字列を分離すると、後で他の言語をサポートしやすくなります。
それ以外の場合、このアプリが一般の人々によって消費されることを意図しており、ユーザーフレンドリーであると想定される場合は、2番目の方法が推奨されます。申し訳ありませんが、これを行うためのより短い方法は本当にわかりません。
このアプリが社内でのみ使用されることを意図している場合は、ショートカット"item(s)"
を実行してください。エンタープライズコードを書くときに、誰かを感動させる必要はありません。ただし、これを公開されているアプリに対して行うことはお勧めしません。これは、プログラマーが怠惰であるという印象を与え、アプリの品質に対する意見を低下させるためです。私を信じてください、この問題のような小さなこと。
メッセージなしでアイテムを削除し、ユーザーに本当に優れた元に戻す機能を提供するだけで、この厄介な複数のすべてを回避できます。 ユーザーは何も読まない 。あなた とにかくあなたのプログラムの一部として良い元に戻す機能を構築する必要があります 。
包括的な元に戻す機能を作成すると、実際には2つのメリットがあります。最初の利点は、ユーザーが間違いを元に戻し、読書を最小限に抑えることを可能にすることで、ユーザーの生活を楽にします。 2番目の利点は、アプリが(間違いだけでなく)重要なワークフローの逆転を可能にすることで、実際の生活を反映していることです。
私はかつて、ダイアログや確認メッセージを1つも使用せずにアプリを作成しました。真剣に考える必要があり、確認タイプのメッセージを使用するよりも実装が非常に困難でした。しかし、エンドユーザーによると、最終結果はかなり使い勝手が良かったです。
どうですか:
string message = "Are you sure you want to delete " + noofitemsselected + " item(s)?"
そうすることで、番号の一致の問題を解消し、ボーナスとして、ユーザーに対してさらに短く、より的確なエラーメッセージを表示することができます。私たちは皆知っています ユーザーはとにかくエラーメッセージを読みません 。それらが短いほど、テキストを少なくとも一瞥する可能性が高くなります。
または、ユーザーがエラーメッセージを読まないというこの知識を武器に、別の方法でこれにアプローチすることもできます。確認メッセージを完全にスキップし、何が削除されたかに関係なく、JustWorksの元に戻す機能を提供するだけです。ほとんどのユーザーは、操作が希望どおりではないことに気付いたときに操作を元に戻すことにすでに慣れており、別の迷惑なポップアップを処理するよりも、この動作の方が自然であると感じる可能性があります。
Java何年もの間持っていたもの:Java.text.MessageFormatとChoiceFormat? http://download.Oracle.com/javase/1.4.2/docs/apiを参照)詳細については、/ Java/text/MessageFormat.html を参照してください。
MessageFormat form = new MessageFormat("The disk \"{1}\" contains {0}.");
form.applyPattern(
"There {0,choice,0#are no files|1#is one file|1<are {0,number,integer} files}.");
Object[] testArgs = {new Long(12373), "MyDisk"};
System.out.println(form.format(testArgs));
// output, with different testArgs
output: The disk "MyDisk" are no files.
output: The disk "MyDisk" is one file.
output: The disk "MyDisk" are 1,273 files.
あなたの場合、あなたはもう少し単純なものが欲しいです:
MessageFormat form = new MessageFormat("Are you sure you want to delete {0,choice,1#one item,1<{0,number.integer} files}?");
このアプローチの利点は、i18nバンドルでうまく機能し、複数形または単数形の概念がない言語(日本語など)に適切に翻訳を提供できることです。
メッセージをハードコーディングするのではなく、別々のリソースファイルで2つのメッセージを提供します。お気に入り
string DELETE_SINGLE = "You have selected {0} item. Are you sure you want to delete it?";
string DELETE_MULTI = "You have selected {0} items. Are you sure you want to delete them?";
次に、それらをString.Formatにフィードします。
if(noofitemsselected == 1)
messageTemplate = MessageResources.DELETE_SINGLE;
else
messageTemplate = MessageResources.DELETE_MULTI;
string message = String.Format(messageTemplate, noofitemsselected)
このアプローチは、ローカライズと保守が簡単だと思います。すべてのUIメッセージは単一の場所にあります。
メッセージの言い回しを変えることで、問題を完全に回避できます。
string message = "The number of selected items is " + noofitemsselected + ". Are you sure you want to delete everything in this selection?";
私が最初に提案するのは、string.Format
を使用することです。これにより、次のようなことができます。
int numOfItems = GetNumOfItems();
string msgTemplate;
msgTemplate = numOfItems == 1 ? "You selected only {0} item." : "Wow, you selected {0} items!";
string msg = string.Format(msgTemplate, numOfItems);
さらに、WPFアプリでは、リソース文字列がパイプで区切られ、単数形と複数形の2つのメッセージ(またはゼロ/単一/多メッセージ)を持つシステムを見てきました。次に、カスタムコンバーターを使用してこのリソースを解析し、関連する(フォーマットされた)文字列を使用できるため、Xamlは次のようになります。
<TextBlock Text="{Binding numOfItems, Converter={StaticResource c:NumericMessageFormatter}, ConverterParameter={StaticResource s:SuitableMessageTemplate}}" />
英語の場合、上記の答えがたくさんあります。他の言語の場合、複数形は名詞の性別と単語の終わりに依存するため、より困難です。フランス語のいくつかの例:
通常の男性:
Vous avez choisi 1 compte. Voulez-vous vraiment le supprimer.
Vous avez choisi 2 comptes. Voulez-vous vraiment les supprimer.
レギュラーフェミニン
Vous avez choisi 1 table. Voulez-vous vraiment la supprimer.
Vous avez choisi 2 tables. Voulez-vous vraiment les supprimer.
不規則な男性(「s」で終わる)
Vous avez choisi 1 pays. Voulez-vous vraiment le supprimer.
Vous avez choisi 2 pays. Voulez-vous vraiment les supprimer?
同じ問題がほとんどのラテン語に存在し、ドイツ語またはロシア語で悪化します。ドイツ語またはロシア語では、3つの性別(マキュリン、フェミニン、ニューター)があります。
あなたの目的が英語以上のものを扱うことであるならば、あなたは注意を払う必要があるでしょう。
適切にローカライズできる複数のメッセージを作成できるようにするには、最初に番号とメッセージの間に間接層を作成するのが賢明だと思います。
たとえば、ある種の定数を使用して、表示するメッセージを指定します。実装の詳細を非表示にする関数を使用してメッセージをフェッチします。
get_message(DELETE_WARNING, quantity)
次に、考えられるメッセージとバリエーションを保持する辞書を作成し、バリエーションをいつ使用すべきかを知らせます。
DELETE_WARNING = {
1: 'Are you sure you want to delete %s item',
>1: 'Are you sure you want to delete %s items'
>5: 'My language has special plural above five, do you wish to delete it?'
}
これで、quantity
に対応するキーを見つけて、そのメッセージでquantity
の値を補間することができます。
この単純化された単純な例ですが、これを実行し、L10NとI18Nに適切なサポートを提供できる他の正しい方法は実際には見当たりません。
以下の関数をVBAからC#に変換する必要がありますが、使用法は次のように変更されます。
int noofitemsselected = SomeFunction();
string message = Pluralize("You have selected # item[s]. Are you sure you want to delete [it/them]?", noofitemsselected);
私はあなたが話していることを正確に行うためにMSAccessで使用するVBA関数を持っています。 VBAを投稿するためにハッキングされることはわかっていますが、とにかくここに行きます。アルゴリズムはコメントから明らかなはずです:
'---------------------------------------------------------------------------------------'
' Procedure : Pluralize'
' Purpose : Formats an English phrase to make verbs agree in number.'
' Usage : Msg = "There [is/are] # record[s]. [It/They] consist[s/] of # part[y/ies] each."'
' Pluralize(Msg, 1) --> "There is 1 record. It consists of 1 party each."'
' Pluralize(Msg, 6) --> "There are 6 records. They consist of 6 parties each."'
'---------------------------------------------------------------------------------------'
''
Function Pluralize(Text As String, Num As Variant, Optional NumToken As String = "#")
Const OpeningBracket = "\["
Const ClosingBracket = "\]"
Const DividingSlash = "/"
Const CharGroup = "([^\]]*)" 'Group of 0 or more characters not equal to closing bracket'
Dim IsPlural As Boolean, Msg As String, Pattern As String
On Error GoTo Err_Pluralize
If IsNumeric(Num) Then
IsPlural = (Num <> 1)
End If
Msg = Text
'Replace the number token with the actual number'
Msg = Replace(Msg, NumToken, Num)
'Replace [y/ies] style references'
Pattern = OpeningBracket & CharGroup & DividingSlash & CharGroup & ClosingBracket
Msg = RegExReplace(Pattern, Msg, "$" & IIf(IsPlural, 2, 1))
'Replace [s] style references'
Pattern = OpeningBracket & CharGroup & ClosingBracket
Msg = RegExReplace(Pattern, Msg, IIf(IsPlural, "$1", ""))
'Return the modified message'
Pluralize = Msg
End Function
Function RegExReplace(SearchPattern As String, _
TextToSearch As String, _
ReplacePattern As String) As String
Dim RE As Object
Set RE = CreateObject("vbscript.regexp")
With RE
.MultiLine = False
.Global = True
.IgnoreCase = False
.Pattern = SearchPattern
End With
RegExReplace = RE.Replace(TextToSearch, ReplacePattern)
End Function
上記のコードコメントで使用法が少し途絶えたので、ここで繰り返します。
Msg = "There [is/are] # record[s]. [It/They] consist[s/] of # part[y/ies] each."
Pluralize(Msg, 1) --> "There is 1 record. It consists of 1 party each."
Pluralize(Msg, 6) --> "There are 6 records. They consist of 6 parties each."
はい、このソリューションは英語以外の言語を無視します。それが重要かどうかは、要件によって異なります。
国際化のサポートが必要だと思います。その場合、言語が異なれば複数形のパターンも異なります(たとえば、2つの特別な複数形、またはポーランド語のようなより複雑な言語)。それを修正します。
GNU Gettextのngettext
関数を使用して、ソースコードで2つの英語メッセージを提供できます。Gettextは、他の言語に翻訳されたときに、他の(場合によってはそれ以上の)メッセージから選択するためのインフラストラクチャを提供します。GNU gettextの複数のサポートの詳細については、 http://www.gnu.org/software/hello/manual/gettext/Plural-forms.html を参照してください。 。
GNUGettextはLGPLの下にあります。 ngettext
の名前はGettextResourceManager.GetPluralString
GettextのC#ポート。
(ローカリゼーションのサポートが不要で、すぐにGettextを使用したくない場合は、英語でこれを行う独自の関数を作成し、2つの完全なメッセージそれに、後でl10nが必要になった場合は、1つの関数を書き直すことで追加できます。)
私の一般的なアプローチは、次のような「単一/複数関数」を作成することです。
public static string noun(int n, string single, string plural)
{
if (n==1)
return single;
else
return plural;
}
次に、メッセージの本文でこの関数を呼び出します。
string message="Congratulations! You have won "+n+" "+noun(n, "foobar", "foobars")+"!";
これはそれほど良いことではありませんが、少なくとも、(a)決定を関数に入れてコードを少し整理し、(b)不規則な複数形を処理するのに十分な柔軟性を備えています。つまり、名詞(n、 "child"、 "children")などを言うのは簡単です。
もちろん、これは英語でのみ機能しますが、概念はより複雑な語尾を持つ言語に容易に拡張できます。
簡単な場合に備えて、最後のパラメーターをオプションにすることができると思います。
public static string noun(int n, string single, string plural=null)
{
if (n==1)
return single;
else if (plural==null)
return single+"s";
else
return plural;
}
もっと一般的な方法はどうですか。 2番目の文での複数化は避けてください。
削除する選択済みアイテムの数:noofitemsselected。 よろしいですか?
このようにすると、行の最後に数字が表示され、簡単に見つけることができます。このソリューションは、どの言語でも同じロジックで機能します。
昨日、私たちのチームのメンバーからまったく同じ質問がありました。
StackOverflowで再び取り上げられたので、私は宇宙がまともなソリューションを作成するのに苦労するように言っていると思いました。
私はすぐに何かをまとめました、そしてそれは決して完璧ではありません、しかしそれは役に立つかもしれません、あるいはsparkいくつかの議論/開発。
このコードは、3つのメッセージが存在する可能性があるという考えに基づいています。次の構造に従う、ゼロアイテム用に1つ、1アイテム用に1つ、複数アイテム用に1つ。
singlePropertyName
singlePropertyName_Zero
singlePropertyName_Plural
リソースクラスを模倣するためにテストする内部クラスを作成しました。実際のリソースファイルを使用してこれをテストしていないため、完全な結果をまだ確認できていません。
これがコードです(現在、3番目のパラメーターを単純にTypeとして指定でき、2番目のパラメーターが文字列であることがわかっているジェネリックをいくつか含めました。これら2つのパラメーターをより良いものに組み合わせる方法があると思いますが、私は '暇なときに戻ってきます。
public static string GetMessage<T>(int count, string resourceSingularName, T resourceType) where T : Type
{
var resourcePluralName = resourceSingularName + "_Plural";
var resourceZeroName = resourceSingularName + "_Zero";
string resource = string.Empty;
if(count == 0)
{
resource = resourceZeroName;
}
else{
resource = (count <= 1)? resourceSingularName : resourcePluralName;
}
var x = resourceType.GetProperty(resource).GetValue(Activator.CreateInstance(resourceType),null);
return x.ToString();
}
テストリソースクラス:
internal class TestMessenger
{
public string Tester{get{
return "Hello World of one";}}
public string Tester_Zero{get{
return "Hello no world";}}
public string Tester_Plural{get{
return "Hello Worlds";}}
}
と私のクイック実行方法
void Main()
{
var message = GetMessage(56, "Tester",typeof(TestMessenger));
message.Dump();
}
次のような関数を書いてみませんか
string GetOutputMessage(int count, string oneItemMsg, string multiItemMsg)
{
return string.Format("{0} {1}", count, count > 1 ? multiItemMsg : oneItemMsg);
}
..そしてあなたが必要なときにそれを使用しますか?
string message = "You have selected " + GetOutputMessage(noofitemsselected,"item","items") + ". Are you sure you want to delete it/them?";
最初の問題、つまり複数化については、 Inflector を使用できます。
2つ目は、ToPronounStringなどの名前の文字列表現拡張機能を使用できます。
World ofWarcraftで行われているようにそれを行います:
BILLING_NAG_WARNING = "Your play time expires in %d |4minute:minutes;";
ユーザーが実際に理解できるメッセージを提示したいのはなぜですか?それは40年のプログラミングの歴史に反します。いいえ、良いことが起こっています。理解できるメッセージでそれを台無しにしないでください。
(j/k)
私はあなたがどんなに素敵なメッセージを伝えたいかによります。最も簡単なものから最も難しいものへ:
複数化を避けるために、エラーメッセージを書き直してください。ユーザーにとってはそれほど良いことではありませんが、より高速です。
より一般的な言葉を使用しますが、それでも番号を含めます。
Railsの「複数化」およびインフレクターシステムを使用して、pluralize(5,'bunch')
と言って5 bunches
を取得できるようにします。 Railsはこれに適したパターンを持っています。
国際化のために、Javaが提供するものを調べる必要があります。これは、2つまたは3つの項目を持つさまざまな形容詞を持つ言語を含むさまざまな言語をサポートします。「s」ソリューションは非常に英語中心です。
どのオプションを選択するかは、製品の目標によって異なります。 -ndp
私の観点からは、最初のソリューションが最も適しています。つまり、複数の言語をサポートするアプリケーションが必要な場合、2番目のオプションは骨の折れる作業になる可能性があります。最初のアプローチでは、多くの労力をかけずにテキストを簡単にローカライズできます。
「選択したアイテムを削除してもよろしいですか」などのより一般的なメッセージを表示できます。
箱から出して少し考えてみますが、ここでの提案はすべて、複数化を行う(そして、複数レベルの複数化、性別などを心配する)か、まったく使用せずに元に戻すことです。
私は非言語的な方法で行き、そのためにビジュアルキューを使用します。例えば指を拭いてアイテムを選択するiPhoneアプリを想像してみてください。マスター削除ボタンを使用してそれらを削除する前に、選択したアイテムを「シェイク」し、V(OK)またはX(キャンセル)ボタンのあるボックスというタイトルの疑問符を表示します。
または、Kinekt/Move/Wiiの3Dの世界では、ファイルを選択し、手を削除ボタンに移動して、確認のために手を頭の上に移動するように指示されることを想像してください(前に述べたのと同じ視覚記号を使用します。たとえば、代わりに3つのファイルを削除するように求められますか?それは、半分透明な赤いXがホバリングしている3つのファイルを表示し、確認するために何かをするように指示します。
私が言及していないアプローチの1つは、置換/選択タグの使用です(たとえば、「{0} [?i({0} = 1):/ cactus/cacti /]を押しつぶそうとしています」などです。 (言い換えると、整数として取られた引数0が1に等しいかどうかに基づいて、フォーマットのような式で置換を指定します).netの前の日にそのようなタグが使用されているのを見ましたが、私は何も知りません。 .netでのそれらの標準であり、それらをフォーマットするための最良の方法もわかりません。
で少し短くなります
string message = "Are you sure you want to delete " + noofitemsselected + " item" + (noofitemsselected>1 ? "s" : "") + "?";