107

多くの場合、ユーザーに表示するメッセージを生成するとき、メッセージには顧客に伝えたいことがいくつか含まれます。

例を挙げましょう: 顧客は 1 から多くの項目を選択し、削除をクリックしました。ここで、顧客に確認メッセージを送りたいと思います。顧客が選択したアイテムの数を伝えたいと思います。顧客が間違いを犯す可能性を最小限に抑えるためです。顧客がいくつかのアイテムを選択し、そのうちの 1 つだけを削除したいときに [削除] をクリックしてしまうことです。彼ら。

1 つの方法は、次のような一般的なメッセージを作成することです。

int noofitemsselected = SomeFunction();
string message = "You have selected " + noofitemsselected + " item(s). Are you sure you want to delete it/them?";

ここでの「問題」は が 1 の場合で、 item と itの代わりにitemitnoofitemselectedと書く必要あります

私の通常の解決策はこのようなものになります

int noofitemsselected = SomeFunction();
string message = "You have selected " + noofitemsselected + " " + (noofitemsselected==1?"item" : "items") + ". Are you sure you want to delete " + (noofitemsselected==1?"it" : "them") + "?";

コード内に多数の数値への参照があり、実際のメッセージが読みにくくなると、これは非常に長くなり、非常に高速になります。

だから私の質問は簡単です。このようなメッセージを生成するより良い方法はありますか?

編集

メッセージはメッセージ ボックス内に表示する必要があると述べた場合、多くの人が非常にハングアップし、単にメッセージ ボックスを使用しないようにする方法について回答しただけで、それで問題ありません。 .

ただし、複数形の問題は、メッセージ ボックスに加えて、プログラム内の他の場所のテキストにも当てはまることに注意してください。たとえば、グリッドで選択された行数を表示するグリッドの横にあるラベルには、複数形に関して同じ問題があります。

したがって、これは基本的にプログラムから何らかの方法で出力されるほとんどのテキストに適用され、解決策はプログラムを変更してテキストを出力しないようにするほど単純ではありません:)

4

25 に答える 25

94

メッセージなしでアイテムを削除し、ユーザーに非常に優れた元に戻す機能を提供することで、この厄介な複数をすべて回避できます。ユーザーは何も読まない。とにかく、プログラムの一部として優れた元に戻す機能を構築する必要があります。

包括的な元に戻す機能を作成すると、実際には 2 つの利点があります。最初の利点は、間違いを元に戻して読む回数を最小限に抑えることで、ユーザーの生活を楽にします。2 つ目の利点は、重要なワークフロー (間違いだけでなく) を元に戻すことができるため、アプリが実際の生活を反映していることです。

ダイアログや確認メッセージを 1 つも使わずにアプリを作成したことがあります。真剣に考える必要があり、確認タイプのメッセージを使用するよりも実装が非常に困難でした。しかし、エンドユーザーによると、最終結果はかなり使いやすかった.

于 2010-11-23T09:11:20.940 に答える
53

どんなに小さなことでも、このアプリを他の言語に翻訳する必要がある場合は、どちらも間違っています。これを行う正しい方法は次のとおりです。

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)"。エンタープライズ コードを書くときに、誰かに感銘を与える必要はありません。しかし、公に消費されるアプリに対してこれを行うことはお勧めしません。これは、プログラマーが怠け者であるという印象を与え、アプリの品質に対する意見を下げるためです。私を信じてください、このような小さなことは問題です。

于 2010-11-23T08:57:00.003 に答える
39

ちょうどどうですか:

string message = "Are you sure you want to delete " + noofitemsselected + " item(s)?"

こうすることで、番号契約の問題を解消し、おまけとして、さらに短く、より的確なエラー メッセージをユーザーに表示することができます。いずれにせよ、ユーザーがエラー メッセージを読まないことは誰もが知っています。短いほど、少なくともテキストをちらりと見る可能性が高くなります。

または、ユーザーはエラー メッセージを読まないという知識があれば、別の方法でアプローチすることもできます。確認メッセージを完全にスキップし、何が削除されたかに関係なく、ただ機能する元に戻す機能を提供します。ほとんどのユーザーは、操作が意図したものではないことに気付いたときに操作を元に戻すことにすでに慣れており、別の迷惑なポップアップに対処するよりも、この動作の方が自然であると感じる可能性があります。

于 2010-11-23T08:42:27.143 に答える
20

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 バンドルとうまく連携し、複数形または単数形の単語の概念がない言語 (日本語など) の翻訳を適切に提供できることです。

于 2010-11-23T13:53:46.570 に答える
12

メッセージをハードコーディングするのではなく、別のリソース ファイルに 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 メッセージは 1 つの場所にあります。

于 2010-11-23T09:00:05.117 に答える
11

メッセージの言い回しを変えることで、この問題を完全に回避できます。

string message = "The number of selected items is " + noofitemsselected + ". Are you sure you want to delete everything in this selection?";
于 2010-11-23T08:43:09.997 に答える
10

私が最初に提案することは、次のとおり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}}" />
于 2010-11-23T08:53:06.640 に答える
7

英語については、上記の多くの回答があります。他の言語では、複数形は名詞の性と語尾に依存するため、より困難です。フランス語でのいくつかの例:

通常の男性:

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 つの性 (男性、女性、中性) があるドイツ語またはロシア語ではさらに悪化します。

目的が英語以上のものを扱うことである場合は、注意が必要です。

于 2010-11-23T12:31:56.023 に答える
5

To be able to have pluralized messages which will be possible to localize properly, my opinion is that it would be wise to first create a layer of indirection between the number and a message.

For example, use a constant of some sort to specify which message you want to display. Fetch the message using some function that will hide the implementation details.

get_message(DELETE_WARNING, quantity)

Next, create a dictionary that holds the possible messages and variations, and make variations know when they should be used.

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?'
}

Now you could simply find the key that corresponds to the quantity and interpolate the value of the quantity with that message.

This oversimplified and naive example, but I don't really see any other sane way to do this and be able to provide good support for L10N and I18N.

于 2010-11-23T16:30:50.063 に答える
5

以下の関数を VBA から C# に変換する必要がありますが、使用法は次のように変わります。

int noofitemsselected = SomeFunction();
string message = Pluralize("You have selected # item[s]. Are you sure you want to delete [it/them]?", noofitemsselected);

あなたが話していることを正確に行うためにMS Accessで使用する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."

はい、このソリューションは英語以外の言語を無視します。それが重要かどうかは、要件によって異なります。

于 2010-11-23T21:55:38.783 に答える
4

国際化

国際化のサポートが必要だと思います。その場合、言語が異なれば複数形のパターンも異なり(たとえば、2つの特別な複数形、またはポーランド語のようなより複雑な言語)、文字列に単純なパターンを適用することに頼ることはできません。それを修正します。

GNU Gettextのngettext関数を使用して、ソースコードで2つの英語のメッセージを提供できます。Gettextは、他の言語に翻訳されたときに、他の(場合によってはそれ以上の)メッセージから選択するためのインフラストラクチャを提供します。GNU gettextの複数形サポートの詳細については、 http: //www.gnu.org/software/hello/manual/gettext/Plural-forms.htmlを参照してください。

GNUGettextはLGPLの下にあります。GettextのC#ポートでngettext名前が付けられています。GettextResourceManager.GetPluralString

(ローカリゼーションのサポートが不要で、すぐにGettextを使用したくない場合は、英語でこれを行う独自の関数を作成し、それに2つの完全なメッセージを渡します。そうすれば、後でl10nが必要になった場合に、次のことができます。単一の関数を書き直して追加します。)

于 2010-11-24T01:48:16.513 に答える
4

より一般的な方法はどうですか。2 番目の文では複数形を避けます。

削除する選択済みアイテムの数: noofitemsselected。
本気ですか?

このようにすると、番号が行末に配置され、非常に簡単に見つけられることがわかりました。このソリューションは、どの言語でも同じロジックで機能します。

于 2010-11-23T09:20:52.683 に答える
4

私の一般的なアプローチは、次のように「単一/複数の関数」を記述することです。

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) 不規則な複数形を処理するのに十分な柔軟性があります。つまり、noun(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;
}
于 2010-11-23T22:22:23.137 に答える
4

複数形を自動的に生成できます。例を参照してください。複数のジェネレーター

複数生成規則については、ウィキペディアを参照してください

string msg = "Do you want to delete " + numItems + GetPlural(" item", numItems) + "?";
于 2010-11-23T08:44:21.263 に答える
3

のような関数を書くのはどうですか

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?";
于 2010-11-23T08:47:39.447 に答える
3

最初の問題については、Pluralize を意味します。Inflector を使用できます

2 つ目は、ToPronounString などの名前の文字列表現拡張を使用できます。

于 2010-11-23T08:48:12.397 に答える
3

昨日、私たちのチームのメンバーからまったく同じ質問が寄せられました.

ここStackOverflowで再び出てきたので、私は宇宙が私にまともな解決策を生み出すためにバッシュをするように言っていると思った.

私はすぐに何かをまとめましたが、決して完璧ではありませんが、役に立つか、議論/開発を引き起こす可能性があります.

このコードは、3 つのメッセージが存在する可能性があるという考えに基づいています。次の構造に従うゼロ項目の場合、1 つの項目の場合、1 つ以上の項目の場合:

singlePropertyName
singlePropertyName_Zero
singlePropertyName_Plural

リソース クラスを模倣するために、テスト用の内部クラスを作成しました。実際のリソース ファイルを使用してこれをまだテストしていないため、完全な結果はまだわかりません。

これがコードです(現在、3番目のパラメーターを単純にタイプとして指定でき、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();
}
于 2010-11-23T12:30:04.710 に答える
2

私はあなたがどんなに素敵なメッセージを伝えたいかによります。最も簡単なものから最も難しいものへ:

  1. 複数化を避けるために、エラーメッセージを書き直してください。ユーザーにとってはそれほど良くはありませんが、より高速です。

  2. より一般的な言葉を使用しますが、それでも番号を含めます。

  3. 「複数化」とインフレクターシステムalaRailsを使用して、言うことができpluralize(5,'bunch')ます5 bunches。Railsはこれに適したパターンを持っています。

  4. 国際化のためには、Javaが提供するものを調べる必要があります。これは、2つまたは3つの項目を持つさまざまな形容詞を持つ言語を含む、さまざまな言語をサポートします。「s」ソリューションは非常に英語中心です。

どのオプションを使用するかは、製品の目標によって異なります。-ndp

于 2010-11-23T15:44:30.910 に答える
2

ユーザーが実際に理解できるメッセージを提示したいのはなぜですか? それは 40 年のプログラミングの歴史に反します。いいえ、私たちは良いことをしています。わかりやすいメッセージでそれを台無しにしないでください。


(j/k)

于 2010-11-24T05:26:43.360 に答える
2

私の観点からは、あなたの最初の解決策が最も適したものです。私がそう言う理由は、複数の言語をサポートするアプリケーションが必要な場合、2 番目のオプションは骨の折れる作業になる可能性があるからです。最初のアプローチでは、それほど労力をかけずにテキストを簡単にローカライズできます。

于 2010-11-23T08:40:58.430 に答える
2

「選択したアイテムを削除してもよろしいですか」のような、より一般的なメッセージを表示できます。

于 2010-11-23T08:41:51.183 に答える
2

World of Warcraft と同じように実行します。

BILLING_NAG_WARNING = "Your play time expires in %d |4minute:minutes;";
于 2010-11-24T08:24:01.787 に答える
0

私が言及していないアプローチの1つは、置換/選択タグの使用です(たとえば、「{0} [?i({0} = 1):/ cactus /cacti/]を押しつぶそうとしています」などです。 (言い換えると、フォーマットのような式で、整数と見なされる引数0が1に等しいかどうかに基づいて置換を指定します).netの前の日にそのようなタグが使用されているのを見たことがありますが、私は何も知りません。 .netでのそれらの標準であり、それらをフォーマットするための最良の方法もわかりません。

于 2010-11-23T18:22:44.247 に答える
0

ここでの提案はすべて、複数形を使用する (複数レベルの複数形、性別などを気にする) か、まったく使用せずに適切な取り消しを提供するかのいずれかです。

私は非言語的な方法で、そのためにビジュアル キューを使用します。たとえば、指を拭いて項目を選択する iPhone アプリを想像してみてください。マスター削除ボタンを使用してそれらを削除する前に、選択したアイテムを「シェイク」し、V (OK) または X (キャンセル) ボタンが付いたボックスというタイトルのクエスチョン マークが表示されます...

または、Kinekt / Move / Wii の 3D ワールドで、ファイルを選択し、手を削除ボタンに移動すると、頭の上に手を移動して確認するように指示されることを想像してください (前に述べたのと同じ視覚記号を使用します。代わりに、代わりに3 つのファイルを削除するように求める場合、3 つのファイルが表示され、ホバリングした半透明の赤い X がオンになり、確認のために何かを行うように指示されます。

于 2010-12-19T06:10:57.513 に答える
0

で少し短くなります

string message = "Are you sure you want to delete " + noofitemsselected + " item" + (noofitemsselected>1 ? "s" : "") + "?";
于 2010-11-23T08:43:30.663 に答える