Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add more localization support #772

Merged
merged 6 commits into from
Feb 22, 2023
Merged
Show file tree
Hide file tree
Changes from 5 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
Original file line number Diff line number Diff line change
Expand Up @@ -455,13 +455,13 @@ public void launch(ClassLoader loader) {
Class<?> c = loader.loadClass(targetClass);
invoker = MethodHandles.lookup().findStatic(c, "main", MethodType.methodType(void.class, String[].class));
} catch (NoSuchMethodException | IllegalAccessException | ClassNotFoundException e) {
throw new FormattedException("Failed to start Minecraft", e);
throw FormattedException.ofLocalized("exception.minecraft.invokeFailure", e);
}

try {
invoker.invokeExact(arguments.toArray());
} catch (Throwable t) {
throw new FormattedException("Minecraft has crashed!", t);
throw FormattedException.ofLocalized("exception.minecraft.generic", t);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ private void init() {
try {
EntrypointUtils.invoke("preLaunch", PreLaunchEntrypoint.class, PreLaunchEntrypoint::onPreLaunch);
} catch (RuntimeException e) {
throw new FormattedException("A mod crashed on startup!", e);
throw FormattedException.ofLocalized("exception.initializerFailure", e);
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/main/java/net/fabricmc/loader/impl/FabricLoaderImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -187,9 +187,9 @@ public void load() {
setup();
} catch (ModResolutionException exception) {
if (exception.getCause() == null) {
throw new FormattedException("Incompatible mod set!", exception.getMessage());
throw FormattedException.ofLocalized("exception.incompatible", exception.getMessage());
} else {
throw new FormattedException("Incompatible mod set!", exception);
throw FormattedException.ofLocalized("exception.incompatible", exception);
}
}
}
Expand Down
29 changes: 29 additions & 0 deletions src/main/java/net/fabricmc/loader/impl/FormattedException.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,12 @@

package net.fabricmc.loader.impl;

import net.fabricmc.loader.impl.util.Localization;

@SuppressWarnings("serial")
public final class FormattedException extends RuntimeException {
private final String mainText;
private String translatedText;

public FormattedException(String mainText, String message) {
super(message);
Expand All @@ -44,7 +47,33 @@ public FormattedException(String mainText, Throwable cause) {
this.mainText = mainText;
}

public static FormattedException ofLocalized(String key, String message) {
return new FormattedException(Localization.formatRoot(key), message).addTranslation(key);
}

public static FormattedException ofLocalized(String key, String format, Object... args) {
return new FormattedException(Localization.formatRoot(key), format, args).addTranslation(key);
}

public static FormattedException ofLocalized(String key, String message, Throwable cause) {
return new FormattedException(Localization.formatRoot(key), message, cause).addTranslation(key);
}

public static FormattedException ofLocalized(String key, Throwable cause) {
return new FormattedException(Localization.formatRoot(key), cause).addTranslation(key);
}

public String getMainText() {
return mainText;
}

/* @Nullable */
apple502j marked this conversation as resolved.
Show resolved Hide resolved
public String getDisplayedText() {
return translatedText == null || translatedText.equals(mainText) ? mainText : translatedText + " (" + mainText + ")";
}

private FormattedException addTranslation(String key) {
this.translatedText = Localization.format(key);
return this;
}
}
11 changes: 6 additions & 5 deletions src/main/java/net/fabricmc/loader/impl/gui/FabricGuiEntry.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import net.fabricmc.loader.impl.gui.FabricStatusTree.FabricStatusTab;
import net.fabricmc.loader.impl.gui.FabricStatusTree.FabricTreeWarningLevel;
import net.fabricmc.loader.impl.util.LoaderUtil;
import net.fabricmc.loader.impl.util.Localization;
import net.fabricmc.loader.impl.util.UrlUtil;
import net.fabricmc.loader.impl.util.log.Log;
import net.fabricmc.loader.impl.util.log.LogCategory;
Expand Down Expand Up @@ -100,7 +101,7 @@ public static void main(String[] args) throws Exception {
public static void displayCriticalError(Throwable exception, boolean exitAfter) {
Log.error(LogCategory.GENERAL, "A critical error occurred", exception);

displayError("Failed to launch!", exception, exitAfter);
displayError(Localization.format("gui.error.header"), exception, exitAfter);
}

public static void displayError(String mainText, Throwable exception, boolean exitAfter) {
Expand All @@ -113,7 +114,7 @@ public static void displayError(String mainText, Throwable exception, boolean ex
exception.printStackTrace(new PrintWriter(error));
}

tree.addButton("Copy error", FabricBasicButtonType.CLICK_MANY).withClipboard(error.toString());
tree.addButton(Localization.format("gui.button.copyError"), FabricBasicButtonType.CLICK_MANY).withClipboard(error.toString());
}, exitAfter);
}

Expand All @@ -123,17 +124,17 @@ public static void displayError(String mainText, Throwable exception, Consumer<F
if (!GraphicsEnvironment.isHeadless() && (provider == null || provider.canOpenErrorGui())) {
String title = "Fabric Loader " + FabricLoaderImpl.VERSION;
FabricStatusTree tree = new FabricStatusTree(title, mainText);
FabricStatusTab crashTab = tree.addTab("Crash");
FabricStatusTab crashTab = tree.addTab(Localization.format("gui.tab.crash"));

if (exception != null) {
crashTab.node.addCleanedException(exception);
} else {
crashTab.node.addMessage("No further details available", FabricTreeWarningLevel.NONE);
crashTab.node.addMessage(Localization.format("gui.error.missingException"), FabricTreeWarningLevel.NONE);
}

// Maybe add an "open mods folder" button?
// or should that be part of the main tree's right-click menu?
tree.addButton("Exit", FabricBasicButtonType.CLICK_ONCE).makeClose();
tree.addButton(Localization.format("gui.button.exit"), FabricBasicButtonType.CLICK_ONCE).makeClose();
treeCustomiser.accept(tree);

try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,8 @@ protected static void handleFormattedException(FormattedException exc) {

GameProvider gameProvider = FabricLoaderImpl.INSTANCE.tryGetGameProvider();

if (gameProvider == null || !gameProvider.displayCrash(actualExc, exc.getMainText())) {
FabricGuiEntry.displayError(exc.getMainText(), actualExc, true);
if (gameProvider == null || !gameProvider.displayCrash(actualExc, exc.getDisplayedText())) {
FabricGuiEntry.displayError(exc.getDisplayedText(), actualExc, true);
} else {
System.exit(1);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ public ClassLoader init(String[] args) {
try {
EntrypointUtils.invoke("preLaunch", PreLaunchEntrypoint.class, PreLaunchEntrypoint::onPreLaunch);
} catch (RuntimeException e) {
throw new FormattedException("A mod crashed on startup!", e);
throw FormattedException.ofLocalized("exception.initializerFailure", e);
}

return cl;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public DependencyOverrides(Path configDir) {
try (JsonReader reader = new JsonReader(new InputStreamReader(Files.newInputStream(path), StandardCharsets.UTF_8))) {
dependencyOverrides = parse(reader);
} catch (IOException | ParseMetadataException e) {
throw new FormattedException("Error parsing dependency overrides!", "Failed to parse " + LoaderUtil.normalizePath(path), e);
throw FormattedException.ofLocalized("exception.parsingOverride", "Failed to parse " + LoaderUtil.normalizePath(path), e);
}
}

Expand Down
19 changes: 15 additions & 4 deletions src/main/java/net/fabricmc/loader/impl/util/Localization.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@
import java.util.ResourceBundle;

public final class Localization {
public static final ResourceBundle BUNDLE = createBundle("net.fabricmc.loader.Messages");
public static final ResourceBundle BUNDLE = createBundle("net.fabricmc.loader.Messages", Locale.getDefault());
public static final ResourceBundle ROOT_LOCALE_BUNDLE = createBundle("net.fabricmc.loader.Messages", Locale.ROOT);

public static String format(String key, Object... args) {
String pattern = BUNDLE.getString(key);
Expand All @@ -38,9 +39,19 @@ public static String format(String key, Object... args) {
}
}

private static ResourceBundle createBundle(String name) {
public static String formatRoot(String key, Object... args) {
String pattern = ROOT_LOCALE_BUNDLE.getString(key);

if (args.length == 0) {
return pattern;
} else {
return MessageFormat.format(pattern, args);
}
}

private static ResourceBundle createBundle(String name, Locale locale) {
if (System.getProperty("java.version", "").startsWith("1.")) { // below java 9
return ResourceBundle.getBundle(name, Locale.getDefault(), new ResourceBundle.Control() {
return ResourceBundle.getBundle(name, locale, new ResourceBundle.Control() {
@Override
public ResourceBundle newBundle(String baseName, Locale locale, String format, ClassLoader loader, boolean reload)
throws IllegalAccessException, InstantiationException, IOException {
Expand All @@ -58,7 +69,7 @@ public ResourceBundle newBundle(String baseName, Locale locale, String format, C
};
});
} else { // java 9 and later
return ResourceBundle.getBundle(name);
return ResourceBundle.getBundle(name, locale);
}
}
}
17 changes: 16 additions & 1 deletion src/main/resources/net/fabricmc/loader/Messages.properties
Original file line number Diff line number Diff line change
@@ -1,9 +1,24 @@
# translation keys used by Fabric Loader
# comments starting with # describe entries, those with ## describe the available arguments
# NOTE: single quotes must be escaped by appending another one (see java.text.MessageFormat)

environment.client=client
environment.server=dedicated server

# error gui
gui.button.copyError=Copy error
gui.button.exit=Exit
gui.error.header=Failed to launch!
gui.error.missingException=No further details available
gui.tab.crash=Crash

# FormattedException main text
exception.incompatible=Incompatible mod set!
exception.parsingOverride=Error parsing dependency overrides!
exception.initializerFailure=A mod crashed on startup!
exception.minecraft.invokeFailure=Failed to start Minecraft!
exception.minecraft.generic=Minecraft has crashed!

# mod resolution errors

resolution.solutionHeader=A potential solution has been determined:
Expand All @@ -26,7 +41,7 @@ resolution.solution.replaceModVersionDifferent.reqSupportedModVersion={0} {1}
## mod versionRange
resolution.solution.replaceModVersionDifferent.reqSupportedModVersions={0}, {1}
##
resolution.solution.replaceModVersionDifferent.unknown=Other constraints that can't be automatically determined
resolution.solution.replaceModVersionDifferent.unknown=Other constraints that can''t be automatically determined
# solution to install a mod that can load in the current environment: Install someMod, any version.
## oldMod newMod versionRange env
resolution.solution.replaceModEnvDisabled=Replace {0} with {2} of {1} that can load in the current environment ({3}) or update/remove the mods depending on it. This happens because a mod wants to load in a {3} environment but depends on another mod that doesn''t load in the {3} environment.
Expand Down
140 changes: 140 additions & 0 deletions src/main/resources/net/fabricmc/loader/Messages_ja_JP.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
# translation keys used by Fabric Loader
# comments starting with # describe entries, those with ## describe the available arguments

environment.client=クライアント
environment.server=サーバー

# error gui
gui.button.copyError=エラーをコピー
gui.button.exit=終了
gui.error.header=起動に失敗しました!
gui.error.missingException=詳細は利用できません。
gui.tab.crash=クラッシュ

# FormattedException main text
exception.incompatible=Modに互換性の問題があります!
exception.parsingOverride=依存関係上書きを読み込めません!
exception.initializerFailure=Modが起動に失敗しました!
exception.minecraft.invokeFailure=Minecraftを開始するのに失敗しました!
exception.minecraft.generic=Minecraftがクラッシュしました!

# mod resolution errors

resolution.solutionHeader=次の解決策を試してみてください:
resolution.depListHeader=満たされていないModの依存関係:
resolution.inactiveMods=アクティブでないMod:

# solution to install a mod: Install someMod, any version.
## mod versionRange
resolution.solution.addMod={0} の {1} を追加する。
## mod version path
resolution.solution.removeMod={0} の {1} ({2}) を除去する。
## oldMods newMod newVersionRange
resolution.solution.replaceMod={0} を {1} {2} で置き換える。
## oldMod newVersionRange
resolution.solution.replaceModVersion={0} を {1} で置き換える。
## oldMod newVersion
resolution.solution.replaceModVersionDifferent={0} を {1} のうち互換性のあるもので置き換える。
## mod version
resolution.solution.replaceModVersionDifferent.reqSupportedModVersion={0} {1}
## mod versionRange
resolution.solution.replaceModVersionDifferent.reqSupportedModVersions={0} {1}
##
resolution.solution.replaceModVersionDifferent.unknown=その他の要件は自動的に判断できませんでした。
# solution to install a mod that can load in the current environment: Install someMod, any version.
## oldMod newMod versionRange env
resolution.solution.replaceModEnvDisabled={0} を現在の環境 ({3}) で読み込み可能な {2} の {1} で置き換えるか、それに依存しているModを更新・除去する。これは、Modが {3} 環境で読み込まれるべきなのに、 {3} 環境で読み込むことのできないModに依存している場合に発生します。
## mod version path
resolution.solution.replaceMod.oldMod={0} {1} ({2})
## mod version
resolution.solution.replaceMod.oldModNoPath={0} {1}

## mod version dep depVersionRange presentVersions presentVersionCount

resolution.depends.envDisabled={0} {1} は {3} の {2} を必要としますが、そのModは現在の環境では無効化されています(クライアント/サーバー専用)!
resolution.depends.missing={0} {1} は {3} の {2} を必要としますが、そのModがありません!
resolution.depends.mismatch={0} {1} は {3} の {2} を必要としますが、異なるバージョンのModが読み込まれています: {4}!
resolution.depends.invalid={0} {1} は {3} の {2} を必要としますが、その他の条件により読み込めません!
resolution.depends.suggestion={3} の {2} を追加しないといけません。

resolution.recommends.envDisabled={0} {1} は {3} の {2} を追加することを推奨していますが、そのModは現在の環境では無効化されています(クライアント/サーバー専用)!
resolution.recommends.missing={0} {1} は {3} の {2} を追加することを推奨していますが、そのModがありません!
resolution.recommends.mismatch={0} {1} は {3} の {2} を追加することを推奨していますが、異なるバージョンのModが読み込まれています: {4}!
resolution.recommends.invalid={0} {1} は {3} の {2} を追加することを推奨していますが、その他の条件により読み込めません!
resolution.recommends.suggestion=最適な使用のために {3} の {2} を追加することを推奨します。

resolution.breaks.invalid={0} {1} は {3} の {2} と互換性がないのに、そのModが存在します: {4}!
resolution.breaks.suggestion={0} の開発者は、このModの組み合わせは動かないと判断しています。片方のModを除去するか、Modの更新を確認してみてください。

resolution.conflicts.invalid={0} {1} は {3} の {2} と同時に使用すると問題を起こすのに、そのModが存在します: {4}!
resolution.conflicts.suggestion=これは読み込みを中断するものではありませんが、 {0} の開発者は、このModの組み合わせは問題を引き起こす可能性があると判断しています。片方のModを除去するか、Modの更新を確認してみてください。

## mod version
resolution.jij.builtin={0} {1} は、読み込み環境を表しているので、セットアップやランチャーの設定を変更しないといけません。
## (no args)
resolution.jij.builtinNoMention=読み込み環境、セットアップやランチャーの設定を変更しないといけません
## mod version path
resolution.jij.root={0} {1} は {2} から読み込まれています
## path
resolution.jij.rootNoMention={0} から読み込まれた
## mod version path
resolution.jij.normal={0} {1} は {2} などから提供されています
## path
resolution.jij.normalNoMention={0} などから提供

## mod version reason
resolution.inactive={0} {1}、 理由: {2}
## (no args)
resolution.inactive.inactive_parent=親Modがアクティブでない
resolution.inactive.incompatible=非互換
resolution.inactive.newer_active=新しいバージョンがアクティブ
resolution.inactive.same_active=同じバージョンがアクティブ
resolution.inactive.to_remove=除去すべきMod
resolution.inactive.to_replace=置き換えるべきMod
resolution.inactive.unknown=不明
resolution.inactive.wrong_environment=環境の不一致 (クライアント/サーバー専用)

resolution.type.mod=Mod

resolution.version.any=いずれかのバージョン
resolution.version.none=満たせないバージョン幅
## version
resolution.version.equal=バージョン {0}
## lowerBound
resolution.version.greater={0} より後のバージョン
## lowerBound
resolution.version.greaterEqual=バージョン {0} またはそれ以上
## upperBound
resolution.version.less={0} より前のバージョン
## upperBound
resolution.version.lessEqual=バージョン {0} またはそれ以下
## majorVersion
resolution.version.major=バージョン {0}.x
## majorVersion minorVersion
resolution.version.majorMinor=バージョン {0}.{1}.x
## lowerBound upperBound
resolution.version.rangeMinIncMaxInc={0} 以上 {1} 以下
resolution.version.rangeMinIncMaxExc={0} 以上 {1} 未満
resolution.version.rangeMinExcMaxInc={0} より後で {1} 以下
resolution.version.rangeMinExcMaxExc={0} より後で {1} 未満

## a b
enumerationAnd.2={0} かつ {1}
## a b c
enumerationAnd.3={0}、 {1}、かつ {2}
## first
enumerationAnd.nPrefix={0}
## allPrev cur
enumerationAnd.n={0}、 {1}
## allPrev last
enumerationAnd.nSuffix={0}、 かつ {1}
## a b
enumerationOr.2={0} または {1}
## a b c
enumerationOr.3={0}、 {1}、または {2}
## first
enumerationOr.nPrefix={0}
## allPrev cur
enumerationOr.n={0}、 {1}
## allPrev last
enumerationOr.nSuffix={0}、 または {1}
Loading