Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 23 additions & 14 deletions forge-core/src/main/java/forge/util/Localizer.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ public class Localizer {
private String currentLanguageRegionID;
private boolean silent = false;
private boolean english = false;
//Keys already reported, so a missing translation is logged once instead of on every lookup
private final Set<String> reportedKeys = Collections.synchronizedSet(new HashSet<>());

public static Localizer getInstance() {
if (instance == null) {
Expand Down Expand Up @@ -82,40 +84,38 @@ public String getMessage(final String key, final Object... messageArguments) {
return getMessage(false, key, messageArguments);
}
public String getMessage(boolean forcedEnglish, final String key, final Object... messageArguments) {
//The flag covers one lookup. Clear it here, or an early return below leaves it set
//and silences every later caller.
final boolean quiet = silent;
silent = false;

MessageFormat formatter = null;
String rawValue = null;

try {
//formatter = new MessageFormat(resourceBundle.getString(key.toLowerCase()), locale);
rawValue = lookup(key, english || forcedEnglish);
formatter = new MessageFormat(rawValue, english || forcedEnglish ? Locale.ENGLISH : locale);
} catch (final IllegalArgumentException | MissingResourceException e) {
if (!silent)
e.printStackTrace();
} catch (final MissingResourceException e) {
warnOnce(quiet, key, "is not translated in " + locale + ", using English");
} catch (final IllegalArgumentException e) {
warnOnce(quiet, key, "is not a valid message pattern in " + locale + ", using English: " + e.getMessage());
}

if (formatter == null) {
if (!silent) {
System.err.println("INVALID PROPERTY: '" + key + "' -- Translation missing from " + locale);
}

if (english || forcedEnglish) {
return "INVALID PROPERTY: '" + key + "' -- Translation missing from English?";
}
try {
formatter = new MessageFormat(englishBundle.getString(key), Locale.ENGLISH);
forcedEnglish = true;
rawValue = englishBundle.getString(key);
formatter = new MessageFormat(rawValue, Locale.ENGLISH);
forcedEnglish = true;
} catch (final IllegalArgumentException | MissingResourceException e) {
if (!silent) {
e.printStackTrace();
}
warnOnce(quiet, key, "is missing from en-US as well: " + e.getMessage());
return "INVALID PROPERTY: '" + key + "' -- Translation missing from English locale?";
}
}

silent = false;

formatter.setLocale(english || forcedEnglish ? Locale.ENGLISH : locale);

String formattedMessage = "CHAR ENCODING ERROR";
Expand Down Expand Up @@ -144,6 +144,14 @@ public String getMessage(boolean forcedEnglish, final String key, final Object..
return formattedMessage;
}

/** Report a bad key once per language. The English fallback keeps the text on screen either way. */
private void warnOnce(final boolean quiet, final String key, final String problem) {
if (quiet || !reportedKeys.add(locale + "/" + key + "/" + problem)) {
return;
}
System.err.println("Localization: '" + key + "' " + problem);
}

public void setLanguage(final String languageRegionID, final String languagesDirectory) {
String[] splitLocale = languageRegionID.split("-");

Expand Down Expand Up @@ -174,6 +182,7 @@ public void setLanguage(final String languageRegionID, final String languagesDir
}

adventureBundle = null;
reportedKeys.clear();

System.out.println("Language '" + resourceBundle.getBaseBundleName() + "' loaded successfully.");

Expand Down
57 changes: 40 additions & 17 deletions forge-gui/res/languages/de-DE.properties
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ cbEnableMusic=Musik aktiviert
lblAudio=Audio
cbAdjustSoundsVolume=Effekt-Lautstärke anpassen
cbAdjustMusicVolume=Musik-Lautstärke anpassen
cbPauseMusicOnFocusLoss=Musik im Hintergrund pausieren
cbAltSoundSystem=Nutze alternatives Sound-System
cbSROptimize=Optimiere Interface für Screenreader
cbUiForTouchScreen=Verbessere Oberfläche für Touchscreens
Expand Down Expand Up @@ -215,7 +216,7 @@ nlManaLostPrompt=Aktiviere, um vor Leerung des Manapools eine Warnung zu erhalte
nlEnforceDeckLegality=Erzwingt eine Deck-Konformität zum gewählten Format (minimale Deckgröße, Anzahl pro Karte im Deck, usw.).
nlpAiSideboardingMode=Wählen Sie die Art der KI-Sideboards: Aus (die KI tritt nicht ins Sideboard), KI (die KI-Sideboards für sich selbst, derzeit meist zufällig), Menschlich für KI (die menschlichen Spieler-Sideboards für die KI in Constructed-Formaten).
nlExperimentalRestore=EXPERIMENTAL - Stores a snapshot to be used for undoing spells or abilities
nlPerformanceMode=Schalten zusätzlich Prüfungen auf statische Fähigkeiten ab, um das Spiel zu beschleunigen. Warnung: Kann Probleme mit 'Aufblitzen' bei Karten von KI-Gegner verursachen!
nlPerformanceMode=Schalten zusätzlich Prüfungen auf statische Fähigkeiten ab, um das Spiel zu beschleunigen. Warnung: Kann Probleme mit ''Aufblitzen'' bei Karten von KI-Gegner verursachen!
nlOrderHand=Order hand by CMC and color
nlFilteredHands=Erzeugt zwei Starthände, und behält die, welche am nächsten an der durchschnittlichen Länderanzahl im Deck ist. (Erfordert Neustart)
nlCloneImgSource=Zeige das originale Kartenbild des Klones statt der geklonten Karte.
Expand Down Expand Up @@ -296,6 +297,7 @@ nlEnableSounds=Geräusche während des Spiels
nlEnableMusic=Hintergrundmusik während des Spiels
nlAdjustSoundsVolume=Effekt-Lautstärke während des Spiels anpassen
nlAdjustMusicVolume=Musik-Lautstärke während des Spiels anpassen
nlPauseMusicOnFocusLoss=Pausiert die Hintergrundmusik, solange das Forge-Fenster nicht im Vordergrund ist
nlAltSoundSystem=Nutze alternatives Sound-System (nur nutzen, wenn es Probleme mit fehlenden Geräuschen gibt)
nlSrOptimize=Setze verschiedene Optionen, um Forge besser für Screenreader lesbar zu machen
KeyboardShortcuts=Tastenkombinationen
Expand Down Expand Up @@ -829,7 +831,7 @@ lblCreate=Erstellen
lblCreateNewPuzzle=Erstelle ein neues Rätsel
lblCreatePuzzleDest1=In diesem Modus beginnst du mit einem leeren Spielfeld und leeren Zonen.
lblCreatePuzzleDest2=Du mußt die Entwickler-Tools nutzen um einen Spielstand für dein Rätsel zu erzeugen.
lblCreatePuzzleDest3=Dann nutze 'Spielstand sichern' um den Stand zu exportieren.
lblCreatePuzzleDest3=Dann nutze ''Spielstand sichern'' um den Stand zu exportieren.
lblCreatePuzzleDest4=Die Export-Datei kann mit einem Text-Editor bearbeitet werden, um den Rätselnamen, die Beschreibung und das Ziel zu ändern.
lblCreatePuzzleDest5=Rätsel-Dateien brauchen die Endung .plz und gehören unter /res/puzzles im Forge-Verzeichnis.
#VSubmenuGauntletLoad.java
Expand Down Expand Up @@ -1486,11 +1488,11 @@ lblMorph=Morph
lblCraft=anzahl der herzustellenden Karten
#PlayerControllerHuman.java
lblFullControl=Full Control
lblFullControlDetails=This feature lets you skip different helpers that streamline gameplay by avoiding somewhat annoying GUI interactions and instead use AI logic to make reasonable decisions for beginners.\nUseful for certain corner cases or if you want to challenge yourself with the Comprehensive Rules:\ne.g. the opposite cost order is needed if activating an animated "Halo Fountain" that's also tapped.
lblFullControlDetails=This feature lets you skip different helpers that streamline gameplay by avoiding somewhat annoying GUI interactions and instead use AI logic to make reasonable decisions for beginners.\nUseful for certain corner cases or if you want to challenge yourself with the Comprehensive Rules:\ne.g. the opposite cost order is needed if activating an animated "Halo Fountain" that''s also tapped.
lblChooseCostOrder=Choose cost order
lblChooseCostReductionOrder=Choose cost reduction order & amount
lblNoPaymentFromManaAbility=Don\'t attempt paying when generating mana
lblNoFreeCombatCostHandling=Don\'t accept attack/block costs of \{0\}
lblNoPaymentFromManaAbility=Don\''t attempt paying when generating mana
lblNoFreeCombatCostHandling=Don\''t accept attack/block costs of \{0\}
lblAllowPaymentStartWithMissingResources=Allow payment start when cost seems unaffordable
lblLayerTimestampOrder=Finetune layer timestamp order
lblOrderCosts=Order costs
Expand Down Expand Up @@ -1730,13 +1732,13 @@ lblEnterNumberGreaterThanOrEqualsToMin=Gib eine Zahl größer oder gleich {0} ei
lblEnterNumberLessThanOrEqualsToMax=Gib eine Zahl kleiner oder gleich {0} ein:
#PlayerOutcome.java
lblWonBecauseAllOpponentsHaveLost=hat gewonnen weil alle Gegner besiegt wurden
lblWonDueToEffectOf=hat durch '%s' gewonnen
lblWonDueToEffectOf=hat durch ''%s'' gewonnen
lblConceded=hat aufgegeben
lblLostTryingToDrawCardsFromEmptyLibrary=hat verloren, da keine Karten mehr in der Bibliothek waren
lblLostBecauseLifeTotalReachedZero=hat verloren, da der Lebenspunktestand kleiner oder gleich Null war
lblLostBecauseOfObtainingTenPoisonCounters=hat durch Anhäufen von 10 oder mehr Giftmarken verloren
lblLostBecauseAnOpponentHasWonBySpell=hat verloren, da ein Gegner durch Spruch '%s' gewonnen hat
lblLostDueToEffectOfSpell=hat durch Spruch '%s' verloren
lblLostBecauseAnOpponentHasWonBySpell=hat verloren, da ein Gegner durch Spruch ''%s'' gewonnen hat
lblLostDueToEffectOfSpell=hat durch Spruch ''%s'' verloren
lblLostDueToAccumulationOf21DamageFromGenerals=hat durch Anhäufen von 21 oder mehr Schadenspunkten durch Generäle verloren
lblAcceptedThatTheGameIsADraw=hat ein Unentschieden akzeptiert
lblLostForUnknownReasonBug=hat aus unbekannten Gründen verloren (Dies ist ein Fehler!)
Expand Down Expand Up @@ -1804,7 +1806,7 @@ lblActivated=aktiviert
lblLogPlayerActionObjectWitchTarget={0} {1} {2} mit Ziel {3}
lblLogPlayerActionObject={0} {1} {2}
lblLogRandomMode={0}''s Zufallsmodus: {1}
lblLogPlayerChosenModeForCard=[0} wählte {1} für {2}
lblLogPlayerChosenModeForCard={0} wählte {1} für {2}
lblLogPlayerHasRestoredControlThemself={0} hat die eigene Kontrolle zurück
lblLogPlayerControlledTargetPlayer={0} wird kontrolliert durch {1}
lblDeathtouch=Todesberührung
Expand Down Expand Up @@ -2051,8 +2053,8 @@ lblDeckInvalid=Ungültiges Deck
lblAlreadyMatchPleaseWait=Es läuft bereits eine Begegnung.\nBevor du versuchst weiterzumachen warte bitte auf das Ende dieser Runde.
#DraftingProcessScreen.java
lblSaveDraftAs=Speichere diesen Draft als
lblAlreadyDeckName=Es existiert bereits ein Deck '
lblOverwriteConfirm='. Überschreiben?
lblAlreadyDeckName=Es existiert bereits ein Deck ''
lblOverwriteConfirm=''. Überschreiben?
lblOverwriteDeck=Deck überschreiben?
lblEndDraftConfirm=Dies wird den laufenden Draft beenden und du kannst nicht zurückkehren.\n\nTrotzdem verlassen?
#Match.java
Expand Down Expand Up @@ -2508,7 +2510,7 @@ lblErrorImportingCube=Error importing cube
lblChooseSealedDeckFormat=Wähle Sealed-Deck-Format
lblSaveCardPoolAs=Speichere diese Sammlung als
lblSaveCardPool=Speichere Sammlung
lblDeckExistsReplaceConfirm='{0}' existiert bereits. Ersetzen?
lblDeckExistsReplaceConfirm=''{0}'' existiert bereits. Ersetzen?
lblSealedDeckGameExists=Sealed-Deck-Spiel existiert
lblChooseAnEdition=Wähle eine Edition
lblChoosePackNumberToPlay=Wähle Packs zum Spielen
Expand Down Expand Up @@ -2670,6 +2672,8 @@ lblSelectingFilter=Wähle Filter...
lblLoadingKeywords=Lade Schlüsselworte...
#LoadSealedScreen.java
lblYouMustSelectExistingSealedPool=Wähle ein vorhandenes Deck oder baue ein Deck aus einem neuen Sealed-Pool.
lblEventDeckEditOnly=Event-Decks lassen sich nur im Editor öffnen.
lblNoOpponentsForEventDeck=Keine KI-Gegner
#LoadGauntletScreen.java
lblYouMustCreateAndSelectGauntlet=Erzeuge und wähle einen Spießrutenlauf.
lblSelectGauntletDeck=Wähle ein Deck für den Spießrutenlauf
Expand Down Expand Up @@ -3022,13 +3026,13 @@ lblInvalidName=Ungültiger Namen
lblInvalidFile=Ungültige Datei
lblCannotAddNewFolderToInvaildFolder=Der neue Ordner kann keinem ungültigen Ordner hinzugefügt werden.
lblEnterNewFolderName=Namen für neuen Ordner eingeben.
lblEnterFolderNameNotValid='{0}' ist kein gültiger Ordnername.
lblEnterFolderNameNotValid=''{0}'' ist kein gültiger Ordnername.
lblNoFolderExistsWithSelectPath=Im gewählten Pfad existiert kein Ordner.
lblNoFileExistsWithSelectPath=Im gewählten Pfad existiert keine Datei.
lblCannotRenameFileInInvalidFolder=Umbenennen unmöglich. Ungültiger Ordner.
lblEnterNewNameForFolder=Neuen Namen für Ordner eingeben.
lblEnterNewNameForFile=Neuen Namen für Datei eingeben.
lblEnterNameNotValid='{0}' ist kein gültiger Name.
lblEnterNameNotValid=''{0}'' ist kein gültiger Name.
lblAreYouSureProceedDelete=Löschung durchführen? Dies kann nicht rückgängig gemacht werden.
lblDeleteFolder=Lösche Ordner
lblDeleteFile=Lösche Datei
Expand Down Expand Up @@ -3073,12 +3077,30 @@ lblNetworkChooseDraftFormat=Choose draft format:
lblNetworkPickTimerPrompt=Pick timer (seconds per pick):
lblNetworkGraceTimerPromptLine1=Grace period on disconnect
lblNetworkGraceTimerPromptLine2=(seconds, 0 to disable):
lblNetworkDraftSettingsTitle=Draft-Einstellungen
lblNetworkPodSizeLabel=Pod-Größe
lblNetworkPodSizeHelp=Leere Plätze werden von KI-Spielern besetzt, die nur am Draft teilnehmen, nicht am Match.
lblNetworkPodSizePrompt=Pod-Größe. Leere Plätze draftet die KI.
lblNetworkPodSizeFloor=Deine Lobby hat bereits {0} Plätze, {0} ist also der kleinste mögliche Pod.
lblNetworkPodSizeEntry={0} Plätze
lblNetworkSetDefaultSuffix={0} (Standard der Auflage)
lblNetworkPicksLabel=Picks pro Booster
lblNetworkPicksHelp=Der Standard der Auflage ist markiert. Alles andere ist eine Hausregel.
lblNetworkPicksPrompt=Picks pro Booster. Der Standard der Auflage ist markiert.
lblNetworkPicksOne=1 Karte
lblNetworkPicksFirst=2 Karten beim ersten Pick
lblNetworkPicksAlways=2 Karten pro Booster
lblNetworkEventPodCaption=Pod
lblNetworkEventPodSummary={0} Plätze, {1}
lblDraftCompletePoolSaved=Draft complete! Your pool has been saved as ''{0}''.
lblDraftOverlayPackOfN=Pack {0} of {1}
lblDraftOverlayPickOfN=Pick {0} of {1}
lblDraftOverlayWaitingForPack=Waiting for pack...
lblDraftOverlayTimer=Timer: {0}
lblDraftOverlayYou=YOU
lblDraftOverlayAllSeats=Alle Plätze
lblDraftOverlaySeat=Platz
lblDraftOverlayPacks=Booster
lblSeatN=Seat {0}
lblDraftLogDraftStarted=Draft started -- {0} players
lblDraftLogPacksOf={0} packs of {1}
Expand All @@ -3089,6 +3111,7 @@ lblDraftLogPassingRight=passing right
lblDraftLogPassingLeft=passing left
lblDraftLogOtherPick={0} picked
lblDraftLogMyPick=You picked: {0} (pack {1}, pick {2})
lblDraftLogMyAutoPick=Automatisch gepickt: {0} (Booster {1}, Pick {2})
lblDraftLogWaiting=[{0} waiting]
lblDraftLogDraftComplete=Draft complete -- {0} cards
lblDraftLogBuildingDeck=Building deck...
Expand Down Expand Up @@ -3429,8 +3452,8 @@ lblDisableWinLose=Deaktivieren Sie Winslose Overlay
lblDisableNotForSaleOverlay=Disable Not For Sale Overlay
lblShowShopOverlay=Shop -Artikelname anzeigen
lblUseAllCardVariants=Use Card Variants from All Sets (Restart Required)
lblPreferEraMatchedTokenArt=Pick Token Art from Edition Closest to Producing Card's Release Date
lblExcludeAlchemyVariants=Exclude variants rebalanced for Arena's Alchemy and Historic formats
lblPreferEraMatchedTokenArt=Pick Token Art from Edition Closest to Producing Card''s Release Date
lblExcludeAlchemyVariants=Exclude variants rebalanced for Arena''s Alchemy and Historic formats
lblGenerateLDADecks=Generate Archetype Decks instead of Genetic AI Decks
lblBindEquipmentLoadoutsToDecks=Ausrüstung an Decks binden
lblDrawChevronsToHiddenEnemiesInClearQuest=Draw chevrons pointing to hidden enemies in a Clear All quest
Expand Down Expand Up @@ -3481,7 +3504,7 @@ lblBoar=Eber
lblBrainiac=Brainiac
lblBringer=Überbringer
lblBrushwagg=Brushwagg
lblC'tan=C'tan
lblC'tan=C''tan
lblCamarid=Kamarid
lblCamel=Kamel
lblCaribou=Karibu
Expand Down
Loading
Loading