From 59e1203b0c52091dd982ff77ff7b705d18ad8c4d Mon Sep 17 00:00:00 2001 From: leriomaggio Date: Mon, 6 Sep 2021 01:45:17 +0100 Subject: [PATCH] Completely redesigned DeckImport with new panel for statistics and full support for i18n --- .../forge/screens/deckeditor/DeckImport.java | 440 +++++++++++++----- forge-gui/res/languages/en-US.properties | 84 +++- 2 files changed, 394 insertions(+), 130 deletions(-) diff --git a/forge-gui-desktop/src/main/java/forge/screens/deckeditor/DeckImport.java b/forge-gui-desktop/src/main/java/forge/screens/deckeditor/DeckImport.java index a9d97818409..532fa8d69a2 100644 --- a/forge-gui-desktop/src/main/java/forge/screens/deckeditor/DeckImport.java +++ b/forge-gui-desktop/src/main/java/forge/screens/deckeditor/DeckImport.java @@ -17,10 +17,13 @@ */ package forge.screens.deckeditor; +import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.WindowEvent; +import java.util.HashMap; import java.util.List; +import java.util.Map; import javax.swing.BorderFactory; import javax.swing.event.DocumentEvent; @@ -28,6 +31,7 @@ import javax.swing.event.DocumentListener; import forge.StaticData; import forge.card.CardEdition; +import forge.card.CardType; import forge.deck.*; import forge.deck.DeckRecognizer.TokenType; import forge.game.GameFormat; @@ -39,7 +43,6 @@ import forge.toolbox.FButton; import forge.toolbox.FCheckBox; import forge.toolbox.FComboBox; import forge.toolbox.FHtmlViewer; -import forge.toolbox.FLabel; import forge.toolbox.FScrollPane; import forge.toolbox.FSkin; import forge.toolbox.FTextArea; @@ -58,63 +61,105 @@ public class DeckImport ex private final FTextArea txtInput = new FTextArea(); private static final String STYLESHEET = ""; // TODO: Add localisation support - private static final String HTML_WELCOME_TEXT = "" + private static final String COLOUR_CODED_TAGS = String.format( + "", + Localizer.getInstance().getMessage("lblGuideKnownCard"), + Localizer.getInstance().getMessage("lblGuideUnknownCard"), + Localizer.getInstance().getMessage("lblGuideIllegalCard"), + Localizer.getInstance().getMessage("lblGuideInvalidCard"), + Localizer.getInstance().getMessage("lblGuideComment") + ); + private static final String TIPS_LIST = String.format( + "", + Localizer.getInstance().getMessage("lblGuideTipsCount", + String.format("%s", Localizer.getInstance().getMessage("lblGuideTipsTitleCount")), + String.format("%s", "4 Power Sink"), + String.format("%s", "4x Power Sink")), + Localizer.getInstance().getMessage("lblGuideTipsSet", + String.format("%s", Localizer.getInstance().getMessage("lblGuideTipsTitleSet"))), + Localizer.getInstance().getMessage("lblGuideTipsCardType", + String.format("%s", Localizer.getInstance().getMessage("lblGuideTipsTitleCardType"))), + Localizer.getInstance().getMessage("lblGuideTipsDeckSection", + String.format("%s", Localizer.getInstance().getMessage("lblGuideTipsTitleDeckSections"))), + Localizer.getInstance().getMessage("lblGuideTipsDeckName", + String.format("%s", Localizer.getInstance().getMessage("lblGuideTipsTitleDeckName"))), + Localizer.getInstance().getMessage("lblGuideTipsDeckFormats", + String.format("%s", Localizer.getInstance().getMessage("lblGuideTipsTitleDeckFormat"))) + ); + + private static final String EXAMPLES_LIST = String.format( +// "" + +// "

%s

" + + "" + + "

%s

" + + "" + + "

%s

" + + "" + + "

%s

" + + "" + + "

%s

", +// Localizer.getInstance().getMessage("lblExample1"), +// Localizer.getInstance().getMessage("nlExample1"), + Localizer.getInstance().getMessage("lblExample2"), + Localizer.getInstance().getMessage("nlExample2"), + Localizer.getInstance().getMessage("lblExample3"), + Localizer.getInstance().getMessage("nlExample3"), + Localizer.getInstance().getMessage("lblExample4"), + Localizer.getInstance().getMessage("nlExample4"), + Localizer.getInstance().getMessage("lblExample5"), + Localizer.getInstance().getMessage("nlExample5") + ); + + private static final String HTML_WELCOME_TEXT = String.format("" + "" + DeckImport.STYLESHEET + "" + "" - + "

How to use the Deck Importer

" + - "

Quick Instructions:\n" + - "Using the Deck Importer is very simple: " + - "just type or paste the names of the cards you want (one per line in the Card List), " + - "and the Importer will automatically create the Decklist with M:TG cards in Forge." + - "You could also specify how many copies of each card you want (default: 1), " + - "and their corresponding Edition.\nIf No Edition is specified, the card print will be " + - "selected automatically according to the Card Art Preference option in Game Settings.\n\n" + - "For example: \"4 Power Sink TMP\" will import:" + - "

" + - "Each line in the list will be processed on-the-fly, and rendered in the Decklist with the following " + - "color-codes:" + - "

" + - "

Additional Options:" + - "

    " + - "
  1. Deck Name: you can specify a name for your deck . Just type " + - "Name: <NAME OF YOUR DECK> in the Card List;
  2. " + - "
  3. Card types: you can organise your list by types, e.g. " + - "Creature, Instant, Land, Sorcery;
  4. " + - "
  5. Deck Section: Similarly, you can organise your list by Deck Sections, " + - "e.g. Main, Sideboard;
  6. " + - "
  7. Collector Number: You can identify a specific Card Print by including its CollNr., " + - "e.g. 20 Island M21 265
  8. " + - "

" + - "

Deck Formats:" + - "Card Lists in the following formats are supported : MTG Arena (.MTGA); " + - "MTG Goldfish (.MTGO); TappedOut; DeckStats.net; .dec files.

" + + "

%s

" + + "

%s

" + + "

%s

" + + "

%s

" + + "

%s

" + + "

%s

" + "" - + ""; + + "", + Localizer.getInstance().getMessage("nlGuideTitle"), + Localizer.getInstance().getMessage("nlGuideQuickInstructions", COLOUR_CODED_TAGS), + Localizer.getInstance().getMessage("nlGuideTipsTitle"), + Localizer.getInstance().getMessage("nlGuideTipsText", TIPS_LIST), + Localizer.getInstance().getMessage("nlGuideExamplesTitle"), + Localizer.getInstance().getMessage("nlGuideExamplesText", EXAMPLES_LIST) + ); private final FHtmlViewer htmlOutput = new FHtmlViewer(DeckImport.HTML_WELCOME_TEXT); + private final FHtmlViewer decklistStats = new FHtmlViewer(); private final FScrollPane scrollInput = new FScrollPane(this.txtInput, false); private final FScrollPane scrollOutput = new FScrollPane(this.htmlOutput, false); - private final FLabel summaryMain = new FLabel.Builder().text(Localizer.getInstance().getMessage("lblImportedDeckSummay")).build(); - private final FLabel summarySide = new FLabel.Builder().text(Localizer.getInstance().getMessage("lblSideboardSummayLine")).build(); + private final FScrollPane scrollStats = new FScrollPane(this.decklistStats, false); + private final FButton cmdCancel = new FButton(Localizer.getInstance().getMessage("lblCancel")); private FButton cmdAccept; // Not initialised as label will be adaptive. @@ -130,7 +175,6 @@ public class DeckImport ex private final ACEditorBase host; private final String IMPORT_CARDS_CMD_LABEL = Localizer.getInstance().getMessage("lblImportCardsCmd"); - private final String IMPORT_DECK_CMD_LABEL = Localizer.getInstance().getMessage("lblImportDeckCmd"); private final String REPLACE_CARDS_CMD_LABEL = Localizer.getInstance().getMessage("lblReplaceCardsCmd"); @@ -146,46 +190,53 @@ public class DeckImport ex List allowedSetCodes = currentGameFormat.getAllowedSetCodes(); this.controller = new DeckImportController(dateTimeCheck, monthDropdown, yearDropdown, currentDeckIsNotEmpty, allowedSetCodes, currentDeckFormat); - String cmdAcceptLabel = currentDeckIsNotEmpty ? IMPORT_CARDS_CMD_LABEL : IMPORT_DECK_CMD_LABEL; + String import_deck_cmd_label = Localizer.getInstance().getMessage("lblImportDeckCmd"); + String cmdAcceptLabel = currentDeckIsNotEmpty ? IMPORT_CARDS_CMD_LABEL : import_deck_cmd_label; this.cmdAccept = new FButton(cmdAcceptLabel); this.host = g; - final int wWidth = 900; - final int wHeight = 900; + initMainPanel(g, currentDeckIsNotEmpty, currentGameType); + } - this.setPreferredSize(new java.awt.Dimension(wWidth, wHeight)); + private void initMainPanel(ACEditorBase g, boolean currentDeckIsNotEmpty, GameType currentGameType) { + GraphicsDevice gd = this.getGraphicsConfiguration().getDevice(); + final int wWidth = (int)(gd.getDisplayMode().getWidth() * 0.7); + final int wHeight = (int)(gd.getDisplayMode().getHeight() * 0.8); + this.setPreferredSize(new Dimension(wWidth, wHeight)); this.setSize(wWidth, wHeight); - String gameTypeName = String.format(" %s", currentGameType.name()); + + String gameTypeName = String.format("For the Game %s", currentGameType.name()); this.setTitle(Localizer.getInstance().getMessage("lblDeckImporter") + gameTypeName); txtInput.setFocusable(true); txtInput.setEditable(true); final FSkin.SkinColor foreColor = FSkin.getColor(FSkin.Colors.CLR_TEXT); - this.scrollInput.setBorder(new FSkin.TitledSkinBorder(BorderFactory.createEtchedBorder(), Localizer.getInstance().getMessage("lblPasteTypeDecklist"), foreColor)); - this.scrollOutput.setBorder(new FSkin.TitledSkinBorder(BorderFactory.createEtchedBorder(), Localizer.getInstance().getMessage("lblExpectRecognizedLines"), foreColor)); + this.scrollInput.setBorder(new FSkin.TitledSkinBorder(BorderFactory.createEtchedBorder(), + Localizer.getInstance().getMessage("lblPasteTypeDecklist"), foreColor)); this.scrollInput.setViewportBorder(BorderFactory.createLoweredBevelBorder()); + + this.scrollOutput.setBorder(new FSkin.TitledSkinBorder(BorderFactory.createEtchedBorder(), + Localizer.getInstance().getMessage("lblExpectRecognizedLines"), foreColor)); this.scrollOutput.setViewportBorder(BorderFactory.createLoweredBevelBorder()); - this.add(this.scrollInput, "cell 0 0, w 50%, growy, pushy"); - this.add(this.dateTimeCheck, "cell 0 2, w 50%, ax c"); - - this.add(monthDropdown, "cell 0 3, w 20%, ax left, split 2, pad 0 4 0 0"); - this.add(yearDropdown, "w 15%"); + this.scrollStats.setBorder(new FSkin.TitledSkinBorder(BorderFactory.createEtchedBorder(), + Localizer.getInstance().getMessage("lblSummaryStats"), foreColor)); + this.scrollStats.setViewportBorder(BorderFactory.createLoweredBevelBorder()); + this.add(this.scrollInput, "cell 0 0, w 45%, growy, pushy"); this.add(this.scrollOutput, "cell 1 0, w 50%, growy, pushy"); - this.add(this.summaryMain, "cell 1 1, label"); - this.add(this.summarySide, "cell 1 2, label"); + this.add(this.scrollStats, "cell 2 0, w 50%, wrap, growy, pushy, pushx"); - if (currentDeckIsNotEmpty){ - // Disabled Default behaviour to replace current deck: bulk import cards into the existing deck! - //this.replaceDeckCheckbox.setSelected(currentDeckIsNotEmpty); - this.add(this.replaceDeckCheckbox, "cell 1 3, align r, ax r"); - } + this.add(this.dateTimeCheck, "cell 0 1, w 50%, ax c"); + this.add(monthDropdown, "cell 0 2, w 20%, ax left, split 2, pad 0 4 0 0"); + this.add(yearDropdown, "cell 0 2, w 20%, ax left, split 2, pad 0 4 0 0"); - this.add(this.cmdAccept, "cell 1 4, split 2, w 150, align r, h 26"); - this.add(this.cmdCancel, "w 150, h 26"); + if (currentDeckIsNotEmpty) + this.add(this.replaceDeckCheckbox, "cell 2 1, w 50%, ax c"); + this.add(this.cmdAccept, "cell 2 2, w 150, align r, h 26"); + this.add(this.cmdCancel, "cell 2 2, w 150, align r, h 26"); this.cmdCancel.addActionListener(new ActionListener() { @Override @@ -206,7 +257,7 @@ public class DeckImport ex // In this way, if this deck will replace the current one, the name will be kept the same! if (!deck.hasName()){ if (currentDeckName.equals("")) - deck.setName("New Deck"); // TODO: try to generate a deck name? + deck.setName(Localizer.getInstance().getMessage("lblNewDeckName")); // TODO: try to generate a deck name? else deck.setName(currentDeckName); } @@ -294,6 +345,7 @@ public class DeckImport ex } else { final StringBuilder sbOut = new StringBuilder(""); sbOut.append(DeckImport.STYLESHEET); + sbOut.append(String.format("

%s

", Localizer.getInstance().getMessage("lblCurrentDecklist"))); for (final DeckRecognizer.Token t : tokens) { sbOut.append(makeHtmlViewOfToken(t)); } @@ -304,83 +356,225 @@ public class DeckImport ex private boolean hasOnlyComment(final List tokens) { for (DeckRecognizer.Token token : tokens) { - if (token.getType() != TokenType.Comment && token.getType() != TokenType.UnknownText) + if (token.getType() != TokenType.COMMENT && token.getType() != TokenType.UNKNOWN_TEXT) return false; } return true; } private void updateSummaries(final List tokens) { - final int[] cardsOk = new int[2]; - final int[] cardsUnknown = new int[2]; - int idx = 0; - for (final DeckRecognizer.Token t : tokens) { - if (t.getType() == TokenType.KnownCard) { - cardsOk[idx] += t.getNumber(); - } - if (t.getType() == TokenType.UnknownCard) { - cardsUnknown[idx] += t.getNumber(); - } - if ((t.getType() == TokenType.DeckSectionName) && t.getText().toLowerCase().contains("side")) { - idx = 1; - } + + String head = ""; + + int unknownCardsCount = 0; + int illegalCardsCount = 0; + int legalCardsCount = 0; + int invalidCardsCount = 0; + + String summaryMsgTemplate = "%s

%s

%s"; + String deckListName = Localizer.getInstance().getMessage("lblDeckListDefaultName"); + + if (hasOnlyComment(tokens)) { + String statsSummaryList = String.format( + "
  • %s
  • %s
  • %s
  • %s
", + Localizer.getInstance().getMessage("lblStatsSummaryCount"), + Localizer.getInstance().getMessage("lblStatsSummarySection"), + Localizer.getInstance().getMessage("lblStatsSummaryCardType"), + Localizer.getInstance().getMessage("lblStatsSummaryCardSet")); + this.decklistStats.setText(String.format(summaryMsgTemplate, head, + Localizer.getInstance().getMessage("lblSummaryHeadMsg", deckListName), + String.format("

%s

", + Localizer.getInstance().getMessage("lblImportedDeckSummary", statsSummaryList)))); } - summaryMain.setText(Localizer.getInstance().getMessage("lblDeckImporterSummaryOfMain", String.valueOf(cardsOk[0]), String.valueOf(cardsUnknown[0]))); - summarySide.setText(Localizer.getInstance().getMessage("lblDeckImporterSummaryOfSideboard", String.valueOf(cardsOk[1]), String.valueOf(cardsUnknown[1]))); - cmdAccept.setEnabled(cardsOk[0] > 0); + else { + Map deckSectionsStats = new HashMap<>(); + deckSectionsStats.put(DeckSection.Main.name(), 0); + String currentKeySection = DeckSection.Main.name(); + Map cardTypeStats = new HashMap<>(); + Map editionsStats = new HashMap<>(); + + for (final DeckRecognizer.Token t : tokens) { + if (t.getType() == TokenType.UNKNOWN_CARD_REQUEST) + unknownCardsCount += t.getNumber(); + else if (t.getType() == TokenType.ILLEGAL_CARD_REQUEST) + illegalCardsCount += t.getNumber(); + else if (t.getType() == TokenType.INVALID_CARD_REQUEST) + invalidCardsCount += t.getNumber(); + else if (t.getType() == TokenType.DECK_SECTION_NAME) + currentKeySection = t.getText(); + else if (t.getType() == TokenType.DECK_NAME) + deckListName = String.format("\"%s\"", t.getText()); + else if (t.getType() == TokenType.LEGAL_CARD_REQUEST) { + int tokenNumber = t.getNumber(); + legalCardsCount += tokenNumber; + + // update deck section stats + int sectionCount = deckSectionsStats.getOrDefault(currentKeySection, 0); + sectionCount += tokenNumber; + deckSectionsStats.put(currentKeySection, sectionCount); + + // update card edition stats + String setCode = t.getCard().getEdition(); + int setCount = editionsStats.getOrDefault(setCode, 0); + setCount += tokenNumber; + editionsStats.put(setCode, setCount); + + // update card type stats + for (CardType.CoreType coreType : t.getCard().getRules().getType().getCoreTypes()) { + String coreTypeName = coreType.name(); + int cTypeCount = cardTypeStats.getOrDefault(coreTypeName, 0); + cTypeCount += tokenNumber; + cardTypeStats.put(coreTypeName, cTypeCount); + } + + } + } + + String deckListSummaryHtml = createSummaryStats(unknownCardsCount, illegalCardsCount, + legalCardsCount, invalidCardsCount, deckSectionsStats, editionsStats, + cardTypeStats); + this.decklistStats.setText(String.format(summaryMsgTemplate, head, + Localizer.getInstance().getMessage("lblSummaryHeadMsg", deckListName), + deckListSummaryHtml)); + } + cmdAccept.setEnabled(legalCardsCount > 0); } -// private static String makeHtmlViewOfToken(final DeckRecognizer.Token token) { -// switch (token.getType()) { -// case KnownCard: -// return String.format("
%s * %s [%s] %s
", token.getNumber(), token.getCard() -// .getName(), token.getCard().getEdition(), token.getCard().isFoil() ? "foil" : ""); -// case UnknownCard: -// return String.format("
%s * %s
", token.getNumber(), token.getText()); -// case SectionName: -// return String.format("
%s
", token.getText()); -// case UnknownText: -// case Comment: -// return String.format("
%s
", token.getText()); -// default: -// return ""; -// } -// } + private String createSummaryStats(int unknownCardsCount, int illegalCardsCount, + int legalCardsCount, int invalidCardsCount, + Map deckSectionsStats, + Map editionsStats, + Map cardTypeStats) { + String cardStatsReport = createCardsStatsReport(unknownCardsCount, illegalCardsCount, + invalidCardsCount, + legalCardsCount, deckSectionsStats); + String editionsReport = createEditionsStatsReport(editionsStats); + String cardTypesReport = createCardTypeStatsReport(cardTypeStats); + + return String.format("
%s
%s" + + "
%s
%s"+ + "
%s
%s", + Localizer.getInstance().getMessage("lblSummaryDeckStats"), cardStatsReport, + Localizer.getInstance().getMessage("lblSummaryCardTypeStats"), cardTypesReport, + Localizer.getInstance().getMessage("lblSummaryEditionStats"), editionsReport); + } + + private String createEditionsStatsReport(Map editionsStats) { + StringBuilder editionsReport = new StringBuilder(); + for (String setCode : editionsStats.keySet()){ + CardEdition edition = StaticData.instance().getCardEdition(setCode); + if (edition == null) + edition = CardEdition.UNKNOWN; + + String tag = String.format("
[%s] %s : %d", setCode, edition.getName(), + editionsStats.get(setCode)); + editionsReport.append(tag); + } + return editionsReport.toString(); + } + + private String createCardTypeStatsReport(Map cardTypesStats) { + StringBuilder cardTypesReport = new StringBuilder(""); + for (String typeLabel : cardTypesStats.keySet()){ + String typeLocalLab = Localizer.getInstance().getMessage(String.format("lbl%s", typeLabel)); + String tag = String.format("
%s : %d
", typeLocalLab, + cardTypesStats.get(typeLabel)); + cardTypesReport.append(tag); + } + return cardTypesReport.toString(); + } + + private String createCardsStatsReport(int unknownCardsCount, int illegalCardsCount, int invalidCardsCount, + int legalCardsCount, Map deckSectionsStats) { + StringBuilder sb = new StringBuilder(); + if (legalCardsCount > 0){ + sb.append(String.format("
%s: %d
", + Localizer.getInstance().getMessage("lblLegalCardsCount"), legalCardsCount)); + } + if (illegalCardsCount > 0){ + sb.append(String.format("
%s: %d
", + Localizer.getInstance().getMessage("lblIllegalCardsCount"), illegalCardsCount)); + } + if (invalidCardsCount > 0){ + sb.append(String.format("
%s: %d
", + Localizer.getInstance().getMessage("lblInvalidCardsCount"), invalidCardsCount)); + } + if (unknownCardsCount > 0){ + sb.append(String.format("
%s: %d
", + Localizer.getInstance().getMessage("lblUnknownCardsCount"), unknownCardsCount)); + } + String cardStatsReport = sb.toString(); + cardStatsReport += createDeckStatsReport(deckSectionsStats); + return cardStatsReport; + } + + private String createDeckStatsReport(Map deckSectionsStats) { + StringBuilder deckSectionsReport = new StringBuilder("
"); + for (String sectionName : deckSectionsStats.keySet()){ + String sectionNameLabel = Localizer.getInstance().getMessage(String.format("lbl%s", sectionName)); + String tag = String.format("
%s : %d
", + Localizer.getInstance().getMessage("lblDeckSectionStats", sectionNameLabel), + deckSectionsStats.get(sectionName)); + deckSectionsReport.append(tag); + } + return deckSectionsReport.toString(); + } private String makeHtmlViewOfToken(final DeckRecognizer.Token token) { + if (token == null) + return ""; + switch (token.getType()) { - case KnownCard: - CardEdition edition = StaticData.instance().getEditions().get(token.getCard().getEdition()); - String editionName; - if (edition == null) - editionName = "Unknown Edition"; - else - editionName = edition.getName(); + case LEGAL_CARD_REQUEST: // TODO: String Padding for alignment - return String.format("
%s x %s from %s (%s) %s
", + return String.format("
%s x %s " + + "(%s) %s %s
", token.getNumber(), token.getCard().getName(), - editionName, token.getCard().getEdition(), - token.getCard().isFoil() ? "foil" : ""); - case UnknownCard: - return String.format("
%s x %s (%s)
", + token.getCard().getEdition(), + token.getCard().getCollectorNumber(), + token.getCard().isFoil() ? "(FOIL)" : ""); + case UNKNOWN_CARD_REQUEST: + return String.format("
%s x %s (%s)
", token.getNumber(), token.getText(), - Localizer.getInstance().getMessage("lblUnknownCard")); - case IllegalCard: - return String.format("
%s x %s (%s %s)
", + Localizer.getInstance().getMessage("lblUnknownCardMsg")); + case ILLEGAL_CARD_REQUEST: + return String.format("
%s x %s (%s %s)
", token.getNumber(), token.getText(), - Localizer.getInstance().getMessage("lblIllegalCard"), + Localizer.getInstance().getMessage("lblIllegalCardMsg"), this.deckFormat.name()); - case DeckSectionName: - return String.format("
%s
", token.getText()); - case CardType: - return String.format("
%s
", token.getText()); - case DeckName: - return String.format("
Deck Name: %s
", token.getText()); - case UnknownText: - case Comment: + case INVALID_CARD_REQUEST: + return String.format("
%s x %s (%s)
", + token.getNumber(), token.getText(), + Localizer.getInstance().getMessage("lblInvalidCardMsg")); + case DECK_SECTION_NAME: + return String.format("
%s
", token.getText()); + case CARD_TYPE: + return String.format("
%s
", token.getText()); + case DECK_NAME: + return String.format("
%s: %s
", + Localizer.getInstance().getMessage("lblDeckName"), + token.getText()); + case UNKNOWN_TEXT: + case COMMENT: default: return ""; - // return String.format("
%s
", token.getText()); +// return String.format("
%s
", token.getText()); } } } diff --git a/forge-gui/res/languages/en-US.properties b/forge-gui/res/languages/en-US.properties index 7904747d980..e1be93022d6 100644 --- a/forge-gui/res/languages/en-US.properties +++ b/forge-gui/res/languages/en-US.properties @@ -2614,21 +2614,91 @@ lblTargetingArcsAlwaysOn=Targeting arcs: Always on #ListCardArea.java lblDone=Done #DeckImport.java -lblSideboardSummayLine=Line for sideboard summary -lblImportedDeckSummay=Imported deck summary will appear here +lblImportedDeckSummary=In this panel, the main Statistics about the Decklist will be automatically reported. These include: {0}. +lblStatsSummaryCount=Statistics on recognized cards +lblStatsSummarySection=The number of cards in each Deck Section (default: "Main") +lblStatsSummaryCardType=The number of cards per (Core) Card Type +lblStatsSummaryCardSet=Statistics on the number of cards per single M:TG Edition lblDeckImporter=Deck Importer lblPasteTypeDecklist=Paste or Type your Card List -lblReplaceDeck=Replace current Deck +lblReplaceDeck=Replace Current Deck lblImportCardsCmd=Import Cards lblImportDeckCmd=Import Deck lblReplaceCardsCmd=Replace Deck lblExpectRecognizedLines=Recognized Decklist -lblDeckImporterSummaryOfMain=Main: {0} cards recognized, {1} unknown cards -lblDeckImporterSummaryOfSideboard=Sideboard: {0} cards recognized, {1} unknown cards -lblIllegalCard=Not Legal in Format -lblUnknownCard=Not existing or Not Found +lblSummaryStats=Decklist Summary +lblSummaryHeadMsg=Summary Statistics for {0} +lblSummaryDeckStats=Deck Statistics +lblSummaryCardTypeStats=Card Types Statistics +lblSummaryEditionStats=Card Editions Statistics +lblDeckSectionStats=Cards in {0} +lblLegalCardsCount=Matched Cards +lblIllegalCardsCount=Illegal Cards +lblUnknownCardsCount=Unknown Cards +lblInvalidCardsCount=Invalid Cards +lblIllegalCardMsg=Not Legal in Format +lblInvalidCardMsg=Not Compliant with Date Restrictions +lblUnknownCardMsg=Unknown Card or Unsupported in Forge +lblNewDeckName=New Deck +lblCurrentDecklist=Current Decklist +lblDeckListDefaultName=Decklist +lblDeckName=Deck Name +lblArtifact=Artifact +lblConspiracy=Conspiracy +lblDungeon=Dungeon +lblSorcery=Sorcery +lblInstant=Instant +lblEnchantment=Enchantment +lblPhenomenon=Phenomenon +lblScheme=Scheme +lblTribal=Tribal #CEditorTokenViewer.java lblAllTokens=All Tokens +# DeckImport Instructions +nlGuideTitle=Instructions +nlGuideQuickInstructions=Using the Deck Importer is very simple: just paste or type the names of the cards you \ + want in the Card List (one per line), and the Importer will start creating the Decklist. \ + Each line in the Decklist will be rendered according to the following color-codes: {0} +lblGuideKnownCard=Card Found, and successfully added to the Decklist. +lblGuideUnknownCard=Card Not Found, or Not Supported in Forge. +lblGuideIllegalCard=Card Not Allowed in Current Deck Format, or Game Type. +lblGuideInvalidCard=Invalid Card (from a Set not compliant with the selected release date, if any) +lblGuideComment=Comment or general text. Ignored by the Importer. +nlGuideTipsTitle=Quick Tips +nlGuideTipsText=A few tips on the supported options to refine and organise your Decklist. {0} +lblGuideTipsCount={0}: specify how many copies of each card to add (default: 1). \n{1} or {2} are both accepted entries. +lblGuideTipsSet={0}: include the code of an M:TG Edition to select a specific Card Art. If necessary, also add in \ + the Collector Number or an Art Index for the card (See examples below). \ + If not specified, the Card Art will be chosen automatically according to the "Card Art Preference" in Game Settings. +lblGuideTipsCardType={0}: organise your Card list by Card Type (e.g. "Land", "Creatures", "Artifacts"). These special \ + lines will appear highlighted in the Decklist. +lblGuideTipsDeckSection={0}: Similarly, the Card List can be organised by Deck Section (e.g. Main, Sideboard, Commander). \ + The keywords will be highlighted in the Decklist, and cards then imported to the specified section (default: Main). +lblGuideTipsDeckName={0}: Assign a name to your card list. Just type "Deck" or "Name", followed by the name you want. \ + This will be used as (new) name for the Deck. +lblGuideTipsDeckFormats={0}: The following Deck formats are supported for immediate import: MTG Arena; \ + MTG Goldfish (all export); TappedOut (all export, but CSV); DeckStats.net; Deck format (.dec) files. +lblGuideTipsTitleCount=Card Amount +lblGuideTipsTitleSet=Card Art +lblGuideTipsTitleCardType=Card Types +lblGuideTipsTitleDeckSections=Deck Sections +lblGuideTipsTitleDeckName=Deck Name +lblGuideTipsTitleDeckFormat=Deck Formats +nlGuideExamplesTitle=Examples +nlGuideExamplesText=Please find below a few examples of some alternative valid entries in the Card List. {0} +lblExample2=10x Islands+ MIR 2 +nlExample2=10 \"Island\" from Set \"Mirage\", 2nd Art, FOIL \n\ \ + (the plus sign after card name indicates a Foil card). +lblExample3=4 Darksteel Citadel (M15) 242 +nlExample3=4 \"Darksteel Citadel\" from \"M15\" with Collector Nr. \"242\"\n \ + (MTGArena format as exported by TappedOut) +lblExample4=18 Forest <254> [THB] (F) +nlExample4=18 \"Forest\" FOIL from \"Theros Beyond Death\" with Collector Nr. \"254\"\n \ + (MTGArena format as exported by MTGGoldfish). The last \"(F)\" is another way to specify foil. +lblExample5=4x TMP|Counterspell +nlExample5=4 copies of \"Counterspell\" from \"Tempest\". \ + In this case, the Set code is specified before the card name using the (optional) pipe separator.\ + Alternative valid separators for set codes are also braces, brackets, and parenthesis. #StartRenderer.java lblClickToAddTargetToFavorites=Click to add {0} to your favorites lblClickToRemoveTargetToFavorites=Click to remove {0} from your favorites