From 52c6b95437312e6904ca6b5b54018118c79b0ff8 Mon Sep 17 00:00:00 2001
From: Colton Flynn <299409214+edgeval@users.noreply.github.com>
Date: Sat, 7 Nov 2026 20:25:21 +0000
Subject: [PATCH] Add Simplified Chinese localization
---
app/src/main/AndroidManifest.xml | 3 +-
app/src/main/java/df/root/D2Status.java | 13 --
app/src/main/java/df/root/MainActivity.java | 194 ++++++++++++------
.../java/df/root/TwoStepProgressView.java | 16 +-
app/src/main/res/layout/activity_main.xml | 44 ++--
app/src/main/res/values-b+zh+Hans/strings.xml | 98 +++++++++
app/src/main/res/values/strings.xml | 100 +++++++++
app/src/main/res/xml/locales_config.xml | 5 +
8 files changed, 366 insertions(+), 107 deletions(-)
create mode 100644 app/src/main/res/values-b+zh+Hans/strings.xml
create mode 100644 app/src/main/res/values/strings.xml
create mode 100644 app/src/main/res/xml/locales_config.xml
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 3dd7c0da..1b60136d 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -12,9 +12,10 @@
diff --git a/app/src/main/java/df/root/D2Status.java b/app/src/main/java/df/root/D2Status.java
index fd14ebe7..6a16deb3 100644
--- a/app/src/main/java/df/root/D2Status.java
+++ b/app/src/main/java/df/root/D2Status.java
@@ -67,19 +67,6 @@ static final class Result {
this.maintenance = maintenance;
this.detail = detail;
}
-
- /** The three submenu rows, worded the way the pill means them. */
- String lockscreenLabel() {
- return lockscreen ? "Yes" : "No";
- }
-
- String maintenanceLabel() {
- return maintenance ? "On" : "Off";
- }
-
- String deviceLabel() {
- return supported ? "Supported" : "Unsupported";
- }
}
/**
diff --git a/app/src/main/java/df/root/MainActivity.java b/app/src/main/java/df/root/MainActivity.java
index 9e960317..280b330a 100644
--- a/app/src/main/java/df/root/MainActivity.java
+++ b/app/src/main/java/df/root/MainActivity.java
@@ -30,6 +30,7 @@
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
+import java.util.Locale;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
@@ -227,28 +228,28 @@ private void driveProgress(String t) {
// v4.0 renamed every marker and dropped the "env adopted" / "partitions set
// ro" confirmations, so these steps are re-keyed to the new table.
if (t.startsWith("kernel module: launching bootstrap")) {
- setSeg2(0.14f, "Verification", 0xFFFFFFFF);
+ setSeg2(0.14f, R.string.progress_verification, 0xFFFFFFFF);
}
if (t.startsWith("bootstrap: loading app preferences file")) {
- setSeg2(0.28f, "Verification", 0xFFFFFFFF);
+ setSeg2(0.28f, R.string.progress_verification, 0xFFFFFFFF);
}
if (t.startsWith("bootstrap: cloning zygote env")
|| t.startsWith("bootstrap: WARNING - clone zygote env failed")) {
- setSeg2(0.42f, "Verification", 0xFFFFFFFF);
+ setSeg2(0.42f, R.string.progress_verification, 0xFFFFFFFF);
}
if (t.startsWith("bootstrap: setting partitions ro")
|| t.startsWith("bootstrap: WARNING - set partitions ro failed")) {
- setSeg2(0.57f, "Verification", 0xFFFFFFFF);
+ setSeg2(0.57f, R.string.progress_verification, 0xFFFFFFFF);
}
if (t.startsWith("bootstrap: disabling ksu modules")) {
- setSeg2(0.71f, "Verification", 0xFFFFFFFF);
+ setSeg2(0.71f, R.string.progress_verification, 0xFFFFFFFF);
}
if (t.startsWith("bootstrap: starting SU daemon")) {
- setSeg2(0.85f, "Verification", 0xFFFFFFFF);
+ setSeg2(0.85f, R.string.progress_verification, 0xFFFFFFFF);
}
if (t.equals("ksud start: SUCCESS")) {
setSeg1(1f);
- setSeg2(1f, "Verified", 0xFFFFFFFF);
+ setSeg2(1f, R.string.progress_verified, 0xFFFFFFFF);
}
if (exploitPhase.equals("cleanup") && t.startsWith("restoring")) {
cleanupSteps++;
@@ -257,11 +258,11 @@ private void driveProgress(String t) {
private void setSeg1(float p) {
seg1 = p;
- binding.twoStep.setSeg1(p, Math.round(p * 100) + "%");
+ binding.twoStep.setSeg1(p, getString(R.string.progress_percent, Math.round(p * 100)));
}
- private void setSeg2(float p, String label, int color) {
- binding.twoStep.setSeg2(p, label, color);
+ private void setSeg2(float p, int labelRes, int color) {
+ binding.twoStep.setSeg2(p, labelRes, color);
}
/** VIS4: the room the stack keeps under the advanced-log card, which is also
@@ -371,7 +372,7 @@ private static String stripHeader(String t) {
java.util.regex.Matcher m = java.util.regex.Pattern
.compile("^===\\s*(.*?)\\s*===$").matcher(t);
if (!m.matches()) return t;
- String h = m.group(1).toUpperCase();
+ String h = m.group(1).toUpperCase(Locale.ROOT);
// The native binary still prints "=== exploit (patching files) ===";
// the UI shows the short form.
if (h.equals("EXPLOIT (PATCHING FILES)")) return "PATCHING";
@@ -395,17 +396,72 @@ private boolean isHeader(String line) {
/** Header lines render big, white and bold; the rest is dimmed. */
private CharSequence styleLogLine(String line) {
- SpannableString ss = new SpannableString(line);
+ SpannableString ss = new SpannableString(localizeLogLine(line));
if (isHeader(line)) {
- ss.setSpan(new StyleSpan(Typeface.BOLD), 0, line.length(), 0);
- ss.setSpan(new RelativeSizeSpan(1.3f), 0, line.length(), 0);
- ss.setSpan(new ForegroundColorSpan(0xFFFFFFFF), 0, line.length(), 0);
+ ss.setSpan(new StyleSpan(Typeface.BOLD), 0, ss.length(), 0);
+ ss.setSpan(new RelativeSizeSpan(1.3f), 0, ss.length(), 0);
+ ss.setSpan(new ForegroundColorSpan(0xFFFFFFFF), 0, ss.length(), 0);
} else {
- ss.setSpan(new ForegroundColorSpan(0xFFB3B3B3), 0, line.length(), 0);
+ ss.setSpan(new ForegroundColorSpan(0xFFB3B3B3), 0, ss.length(), 0);
}
return ss;
}
+ /** Localize display text after parsing so stored and exported logs keep their original format. */
+ private String localizeLogLine(String line) {
+ switch (line) {
+ case LOG_HEADER: return getString(R.string.log_system);
+ case "SETUP": return getString(R.string.log_setup);
+ case "PATCHING": return getString(R.string.log_patching);
+ case "INIT": return getString(R.string.log_init);
+ case "CLEANUP": return getString(R.string.log_cleanup);
+ case "EXPLOIT SUCCESS": return getString(R.string.log_success);
+ case "already hooked, skipping": return getString(R.string.log_already_hooked);
+ case "ERROR: Developer Options → 'Log buffer size' is set to Off.":
+ return getString(R.string.log_buffer_off);
+ case "Change it to any other value and retry.": return getString(R.string.log_buffer_retry);
+ case "no SU manager selected": return getString(R.string.log_no_manager);
+ case "patch: DEVICE NOT VULNERABLE": return getString(R.string.log_not_vulnerable);
+ default: break;
+ }
+
+ String removed = " is no longer installed";
+ if (line.endsWith(removed)) {
+ return getString(R.string.log_manager_removed,
+ line.substring(0, line.length() - removed.length()));
+ }
+ String missingKsud = "libksud.so not found in ";
+ if (line.startsWith(missingKsud)) {
+ return getString(R.string.log_ksud_not_found, line.substring(missingKsud.length()));
+ }
+
+ int separator = line.indexOf(": ");
+ if (separator < 0) return line;
+ String value = line.substring(separator + 2);
+ switch (line.substring(0, separator)) {
+ case "app version": return getString(R.string.log_app_version, value);
+ case "manufacturer": return getString(R.string.log_manufacturer, value);
+ case "model": return getString(R.string.log_model, value);
+ case "android version": return getString(R.string.log_android_version, value);
+ case "security patch": return getString(R.string.log_security_patch, value);
+ case "kernel version": return getString(R.string.log_kernel_version, value);
+ case "exception": return getString(R.string.log_exception, value);
+ case "EXPLOIT FAILED": return getString(R.string.log_failure, localizeFailureReason(value));
+ default: return line;
+ }
+ }
+
+ private String localizeFailureReason(String reason) {
+ switch (reason) {
+ case "READING PREFS FAILED": return getString(R.string.log_reason_prefs);
+ case "DISABLE MODULES FAILED": return getString(R.string.log_reason_modules);
+ case "KSUD NONZERO EXIT": return getString(R.string.log_reason_ksud);
+ case "CHECK LOGCAT & DMESG": return getString(R.string.log_reason_check_logs);
+ case "FAILED TO PATCH FILES": return getString(R.string.log_reason_patch);
+ default: return reason;
+ }
+ }
+
private void saveLog() {
try (FileOutputStream out = new FileOutputStream(lastLogFile)) {
out.write(logBuffer.toString().getBytes(java.nio.charset.StandardCharsets.UTF_8));
@@ -445,7 +501,7 @@ private String readLastLog() {
// carried both overloads since the first API level.
StringBuffer sb = new StringBuffer();
while (m.find()) m.appendReplacement(sb, java.util.regex.Matcher
- .quoteReplacement(m.group().toLowerCase()));
+ .quoteReplacement(m.group().toLowerCase(Locale.ROOT)));
m.appendTail(sb);
return sb.toString();
} catch (IOException e) {
@@ -533,8 +589,8 @@ protected void onCreate(Bundle savedInstanceState) {
.getBoolean("disable_modules", false));
moduleRefresh = false;
binding.modulesSubtitle.setText(binding.switchModules.isChecked()
- ? "Enabled - Active at next reroot"
- : "Disabled - No Modules on reroot");
+ ? R.string.modules_enabled
+ : R.string.modules_disabled);
binding.switchModules.setOnCheckedChangeListener((btn, on) -> {
if (moduleRefresh) return;
btn.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP);
@@ -566,7 +622,7 @@ protected void onCreate(Bundle savedInstanceState) {
// been set, because that is the combination the headline renders
// in - and the update pill has to be exactly this wide to cover it.
headlinePx = Math.round(
- titleView.getPaint().measureText("DirtyFrag"));
+ titleView.getPaint().measureText(getString(R.string.app_name)));
}
}
});
@@ -621,10 +677,10 @@ && createDeviceProtectedStorageContext()
.getSharedPreferences("dfroot", MODE_PRIVATE)
.getBoolean("last_run_success", false);
boolean lastFailed = hasLastLog
- && last.toLowerCase().contains("=== exploit failed");
+ && last.toLowerCase(Locale.ROOT).contains("=== exploit failed");
if (rootedNow || lastSuccess) {
- binding.twoStep.setSeg1(1f, "100%");
- binding.twoStep.setSeg2(1f, "Verified", 0xFFFFFFFF);
+ binding.twoStep.setSeg1(1f, getString(R.string.progress_percent, 100));
+ binding.twoStep.setSeg2(1f, R.string.progress_verified, 0xFFFFFFFF);
} else if (lastFailed) {
setFailedState();
}
@@ -635,14 +691,14 @@ && createDeviceProtectedStorageContext()
if (!runArmed) {
runArmed = true;
v.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP);
- binding.btnRun.setText("Are you sure");
+ binding.btnRun.setText(R.string.run_confirm);
return;
}
runArmed = false;
v.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP);
running = true;
binding.btnRun.setEnabled(false);
- binding.btnRun.setText("Running");
+ binding.btnRun.setText(R.string.running);
// Same dark greyed-out styling as the Rooted state.
binding.btnRun.setTextColor(0xFF6E6E6E);
binding.btnRun.setBackgroundTintList(ColorStateList.valueOf(0xFF1F1F1F));
@@ -700,7 +756,7 @@ && createDeviceProtectedStorageContext()
// -- Open Github Page row --
TextView ghRow = new TextView(this);
ghRow.setBackgroundResource(R.drawable.menu_row_highlight);
- ghRow.setText("Open Github Page");
+ ghRow.setText(R.string.open_github);
ghRow.setGravity(android.view.Gravity.CENTER_VERTICAL | android.view.Gravity.START);
ghRow.setPadding((int) (20 * md), 0, 0, 0);
ghRow.setTextColor(0xFFE8E8E8);
@@ -723,7 +779,7 @@ && createDeviceProtectedStorageContext()
// word in it. --
TextView rlRow = new TextView(this);
rlRow.setBackgroundResource(R.drawable.menu_row_highlight);
- rlRow.setText("Reset UI Layout");
+ rlRow.setText(R.string.reset_ui_layout);
rlRow.setGravity(android.view.Gravity.CENTER_VERTICAL | android.view.Gravity.START);
rlRow.setPadding((int) (20 * md), 0, 0, 0);
rlRow.setTextColor(0xFFE8E8E8);
@@ -753,7 +809,7 @@ && createDeviceProtectedStorageContext()
exRow.setPadding((int) (20 * md), 0, (int) (20 * md), 0);
exRow.setBackgroundResource(R.drawable.menu_row_highlight);
TextView exText = new TextView(this);
- exText.setText("Expert Mode");
+ exText.setText(R.string.expert_mode);
exText.setTextColor(0xFFE8E8E8);
exText.setTextSize(android.util.TypedValue.COMPLEX_UNIT_SP, 15);
exRow.addView(exText, new android.widget.LinearLayout.LayoutParams(
@@ -842,8 +898,8 @@ && createDeviceProtectedStorageContext()
* disabled toggles, titles suffixed with (Expert). */
private void applyExpertMode() {
boolean ok = expertMode;
- binding.tvAutorunTitle.setText(ok ? "Autorun" : "Autorun (Expert)");
- binding.tvRebootTitle.setText(ok ? "Auto reboot" : "Auto reboot (Expert)");
+ binding.tvAutorunTitle.setText(ok ? R.string.autorun : R.string.autorun_expert);
+ binding.tvRebootTitle.setText(ok ? R.string.auto_reboot : R.string.auto_reboot_expert);
binding.tvAutorunTitle.setTextColor(ok ? 0xFFFFFFFF : 0xFF6E6E6E);
binding.tvAutorunDesc.setTextColor(ok ? 0xFF9E9E9E : 0xFF5A5A5A);
binding.tvRebootTitle.setTextColor(ok ? 0xFFFFFFFF : 0xFF6E6E6E);
@@ -949,7 +1005,7 @@ public void onCardSelectionCleared() {
b.setImageTintList(ColorStateList.valueOf(0xFF3A3A3C));
b.setScaleType(android.widget.ImageView.ScaleType.CENTER);
b.setPadding(0, 0, 0, 0);
- b.setContentDescription("Hide card");
+ b.setContentDescription(getString(R.string.hide_card));
// Above the lifted card (18dp), below the menu dim layer (30dp).
b.setElevation(26 * md);
b.setVisibility(View.GONE);
@@ -1223,8 +1279,8 @@ private void applyModuleState(boolean on) {
// The subtitle is the whole message. Nothing is announced over the bottom
// of the screen for a switch the user can already see move.
binding.modulesSubtitle.setText(on
- ? "Enabled - Active at next reroot"
- : "Disabled - No Modules on reroot");
+ ? R.string.modules_enabled
+ : R.string.modules_disabled);
}
// ---- SU manager picker --------------------------------------------------
@@ -1274,8 +1330,8 @@ private void loadSuManagerPref() {
if (suManagerPkg == null) sp.edit().remove("su_manager").apply();
}
binding.suManagerSubtitle.setText(suManagerPkg == null
- ? "Not selected - required"
- : suManagerLabel + " (" + suManagerPkg + ")");
+ ? getString(R.string.su_manager_required)
+ : getString(R.string.su_manager_selected, suManagerLabel, suManagerPkg));
binding.suManagerSubtitle.setTextColor(suManagerPkg == null
? 0xFFE57373 : 0xFF9E9E9E);
}
@@ -1294,7 +1350,7 @@ private void showSuManagerPopup() {
if (apps.isEmpty()) {
TextView none = new TextView(this);
- none.setText("No app with libksud.so found");
+ none.setText(R.string.su_manager_not_found);
none.setGravity(android.view.Gravity.CENTER_VERTICAL | android.view.Gravity.START);
none.setPadding((int) (20 * md), (int) (12 * md), (int) (20 * md), (int) (12 * md));
none.setTextColor(0xFF6E6E6E);
@@ -1398,7 +1454,7 @@ private void selectSuManager(SuManagerEntry e) {
.edit().putString("su_manager", e.packageName).apply();
suManagerPkg = e.packageName;
suManagerLabel = e.label;
- binding.suManagerSubtitle.setText(e.label + " (" + e.packageName + ")");
+ binding.suManagerSubtitle.setText(getString(R.string.su_manager_selected, e.label, e.packageName));
binding.suManagerSubtitle.setTextColor(0xFF9E9E9E);
updateRunButton();
}
@@ -1421,7 +1477,7 @@ private void updateRunButton() {
}
boolean ok = suManagerPkg != null;
binding.btnRun.setEnabled(ok);
- binding.btnRun.setText("Run exploit");
+ binding.btnRun.setText(R.string.run_exploit);
binding.btnRun.setTextColor(ok ? 0xFFE0E0E0 : 0xFF6E6E6E);
binding.btnRun.setBackgroundTintList(ColorStateList.valueOf(ok ? 0xFF7A7A7A : 0xFF1F1F1F));
binding.btnRun.setStrokeColor(
@@ -1451,7 +1507,7 @@ private void showUpdatePill() {
if (word <= 0) {
// The font failed to load, so no title paint measured the headline. A
// pill that just fits its own word is honest; a guessed width is not.
- word = Math.round(binding.updateLabel.getPaint().measureText("Update"));
+ word = Math.round(binding.updateLabel.getPaint().measureText(getString(R.string.update)));
}
// Wider than the word it replaces. The headline's advance is exactly as wide
// as a word, and a capsule cut to that reads as a badge clipped to its own
@@ -1865,13 +1921,18 @@ private void showRecoveryPopup() {
box.setBackgroundResource(R.drawable.popup_bg);
box.setPadding(0, (int) (6 * md), 0, (int) (6 * md));
- box.addView(odinRow("DL/Recovery", dlRecoveryLabel(shown, vault), verdictValue), odinRowLp(md));
+ box.addView(odinRow(R.string.recovery_download, dlRecoveryLabel(shown, vault), verdictValue),
+ odinRowLp(md));
box.addView(odinSep(md), odinSepLp(md));
- box.addView(odinRow("Lockscreen", shown.lockscreenLabel()), odinRowLp(md));
+ box.addView(odinRow(R.string.recovery_lockscreen,
+ getString(shown.lockscreen ? R.string.recovery_yes : R.string.recovery_no)), odinRowLp(md));
box.addView(odinSep(md), odinSepLp(md));
- box.addView(odinRow("Maintenance Mode", shown.maintenanceLabel()), odinRowLp(md));
+ box.addView(odinRow(R.string.recovery_maintenance,
+ getString(shown.maintenance ? R.string.recovery_on : R.string.recovery_off)), odinRowLp(md));
box.addView(odinSep(md), odinSepLp(md));
- box.addView(odinRow("Device", shown.deviceLabel()), odinRowLp(md));
+ box.addView(odinRow(R.string.recovery_device,
+ getString(shown.supported ? R.string.recovery_supported : R.string.recovery_unsupported)),
+ odinRowLp(md));
// Whether either switch below can be moved at all. Expert Mode arms them; the firmware has
// to have something for them to do. A device whose firmware never gates download mode has
@@ -1889,7 +1950,7 @@ private void showRecoveryPopup() {
box.addView(odinSep(md), odinSepLp(md));
final OneUiSwitch[] writeSw = new OneUiSwitch[1];
final TextView[] writeName = new TextView[1];
- box.addView(odinToggleRow(switchName("Recovery Writer", writerLive, shown.supported),
+ box.addView(odinToggleRow(switchName(R.string.recovery_writer, writerLive, shown.supported),
vault != null && vault.ok && vault.atCommand == 1, writerLive,
writeSw, writeName), odinRowLp(md));
if (writerLive) {
@@ -1898,10 +1959,10 @@ private void showRecoveryPopup() {
// a root manager that is not answering must not be flippable into a loop of su
// calls. Its answer is the only thing allowed to move the thumb again.
writeSw[0].setEnabled(false);
- writeName[0].setText(want ? "Writing flag ..." : "Clearing flag ...");
+ writeName[0].setText(want ? R.string.recovery_writing : R.string.recovery_clearing);
writeDmcFlag(want, answered -> {
verdictValue[0].setText(dlRecoveryLabel(shown, answered));
- writeName[0].setText(switchName("Recovery Writer", writerLive, shown.supported));
+ writeName[0].setText(switchName(R.string.recovery_writer, writerLive, shown.supported));
// What the vault answered, not what was asked for. The firmware-that-does-not
// -gate path hands back the vault as it was, which before a read is no vault
// at all - in which case the honest position is off.
@@ -1936,8 +1997,10 @@ private void showRecoveryPopup() {
// owner has costs one suffix on a row they are already reading, and saves them finding out
// by rebooting.
final boolean wantAuto = sp.getBoolean(PREF_D2_AUTO, false);
- String autoName = switchName("Autorun Writer", writerLive, shown.supported);
- if (wantAuto && writerLive && !bootPathEnabled()) autoName += " (at reroot)";
+ String autoName = switchName(R.string.recovery_autorun_writer, writerLive, shown.supported);
+ if (wantAuto && writerLive && !bootPathEnabled()) {
+ autoName = getString(R.string.recovery_at_reroot, autoName);
+ }
box.addView(odinToggleRow(autoName,
wantAuto, writerLive, autoSw, null), odinRowLp(md));
autoSw[0].setOnCheckedChangeListener((view, checked) ->
@@ -2055,8 +2118,8 @@ public void getOutline(View view, android.graphics.Outline outline) {
* its near-white, so the half of the pair that can only be read is the dimmer
* one and the answer is what the eye lands on.
*/
- private android.widget.LinearLayout odinRow(String label, String value) {
- return odinRow(label, value, null);
+ private android.widget.LinearLayout odinRow(int labelRes, String value) {
+ return odinRow(labelRes, value, null);
}
/**
@@ -2064,7 +2127,7 @@ private android.widget.LinearLayout odinRow(String label, String value) {
* can change need that: the answer has to be painted into the menu that is already open,
* not left describing the vault as it was before the press.
*/
- private android.widget.LinearLayout odinRow(String label, String value,
+ private android.widget.LinearLayout odinRow(int labelRes, String value,
android.widget.TextView[] slot) {
float md = getResources().getDisplayMetrics().density;
android.widget.LinearLayout row = new android.widget.LinearLayout(this);
@@ -2073,7 +2136,7 @@ private android.widget.LinearLayout odinRow(String label, String value,
row.setPadding((int) (20 * md), 0, (int) (20 * md), 0);
TextView name = new TextView(this);
- name.setText(label);
+ name.setText(labelRes);
name.setTextColor(0xFF9E9E9E);
name.setTextSize(android.util.TypedValue.COMPLEX_UNIT_SP, 15);
row.addView(name, new android.widget.LinearLayout.LayoutParams(
@@ -2171,10 +2234,12 @@ private static android.widget.LinearLayout.LayoutParams odinSepLp(float md) {
* writer switch controls is not given a row of its own any more; its thumb is that byte, on the
* same card.
*/
- private static String dlRecoveryLabel(D2Status.Result est, D2Writer.Vault v) {
- if (!est.supported) return "Not gated here";
- if (v != null && v.ok) return v.gated() ? "D2 Blocked" : "Allowed";
- return "Unknown";
+ private String dlRecoveryLabel(D2Status.Result est, D2Writer.Vault v) {
+ if (!est.supported) return getString(R.string.recovery_not_gated);
+ if (v != null && v.ok) {
+ return getString(v.gated() ? R.string.recovery_blocked : R.string.recovery_allowed);
+ }
+ return getString(R.string.recovery_unknown);
}
/**
@@ -2184,9 +2249,10 @@ private static String dlRecoveryLabel(D2Status.Result est, D2Writer.Vault v) {
* armed yet" and "will never be live here" is the difference between coming back later and
* coming back never.
*/
- private static String switchName(String base, boolean live, boolean supported) {
- if (!supported) return base + " (no gate)";
- return live ? base : base + " (Expert)";
+ private String switchName(int nameRes, boolean live, boolean supported) {
+ String base = getString(nameRes);
+ if (!supported) return getString(R.string.recovery_no_gate, base);
+ return live ? base : getString(R.string.recovery_expert, base);
}
@@ -2197,10 +2263,10 @@ private static String switchName(String base, boolean live, boolean supported) {
private void setFailedState() {
failedRun = true;
binding.twoStep.setFailed(true);
- binding.twoStep.setSeg1(seg1, "Failure");
- binding.twoStep.setSeg2(1f, "Reboot", 0xFFE57373);
+ binding.twoStep.setSeg1(seg1, getString(R.string.progress_failure));
+ binding.twoStep.setSeg2(1f, R.string.progress_reboot, 0xFFE57373);
binding.btnRun.setEnabled(false);
- binding.btnRun.setText("Run exploit");
+ binding.btnRun.setText(R.string.run_exploit);
binding.btnRun.setTextColor(0xFF6E6E6E);
binding.btnRun.setBackgroundTintList(ColorStateList.valueOf(0xFF1F1F1F));
binding.btnRun.setStrokeColor(ColorStateList.valueOf(0xFF1F1F1F));
@@ -2216,7 +2282,7 @@ private void setRootedState(boolean rooted) {
int bg = (rooted || !ok) ? 0xFF1F1F1F : 0xFF7A7A7A;
int stroke = (rooted || !ok) ? 0xFF1F1F1F : 0xFFA6A6A6;
binding.btnRun.setEnabled(ok);
- binding.btnRun.setText(rooted ? "Rooted" : "Run exploit");
+ binding.btnRun.setText(rooted ? R.string.rooted : R.string.run_exploit);
binding.btnRun.setTextColor(fg);
binding.btnRun.setBackgroundTintList(ColorStateList.valueOf(bg));
binding.btnRun.setStrokeColor(ColorStateList.valueOf(stroke));
@@ -2473,7 +2539,7 @@ private void runExploit() {
boolean rooted = new File("/dev/df").exists();
if (rooted) {
setRootedState(true);
- binding.twoStep.setSeg2(1f, "Verified", 0xFFFFFFFF);
+ binding.twoStep.setSeg2(1f, R.string.progress_verified, 0xFFFFFFFF);
} else {
setFailedState();
setRootedState(false);
diff --git a/app/src/main/java/df/root/TwoStepProgressView.java b/app/src/main/java/df/root/TwoStepProgressView.java
index 8538cf09..905a852d 100644
--- a/app/src/main/java/df/root/TwoStepProgressView.java
+++ b/app/src/main/java/df/root/TwoStepProgressView.java
@@ -30,8 +30,8 @@ public class TwoStepProgressView extends View {
private float seg1 = 0f;
private float seg2 = 0f;
- private String leftLabel = "Waiting";
- private String rightLabel = "Verification";
+ private String leftLabel;
+ private String rightLabel;
private int rightColor = 0xFF8E8E8E;
private boolean failed;
private float greyMix = 0f;
@@ -41,6 +41,8 @@ public class TwoStepProgressView extends View {
public TwoStepProgressView(Context context, AttributeSet attrs) {
super(context, attrs);
+ leftLabel = context.getString(R.string.progress_waiting);
+ rightLabel = context.getString(R.string.progress_verification);
textPaint.setTextAlign(Paint.Align.CENTER);
}
@@ -51,11 +53,11 @@ public void setSeg1(float p, String label) {
invalidate();
}
- public void setSeg2(float p, String label, int labelColor) {
+ public void setSeg2(float p, int labelRes, int labelColor) {
seg2 = clamp(p);
- rightLabel = label;
+ rightLabel = getContext().getString(labelRes);
rightColor = labelColor;
- if ("Verified".equals(label) && seg1 >= 1f) {
+ if (labelRes == R.string.progress_verified && seg1 >= 1f) {
scheduleGreyOut();
} else {
cancelGreyOut();
@@ -73,8 +75,8 @@ public void setFailed(boolean f) {
public void reset() {
cancelGreyOut();
failed = false;
- setSeg1(0f, "Waiting");
- setSeg2(0f, "Verification", 0xFFFFFFFF);
+ setSeg1(0f, getContext().getString(R.string.progress_waiting));
+ setSeg2(0f, R.string.progress_verification, 0xFFFFFFFF);
}
/** When the bar reaches 100% + Verified, ease the fill from blue to grey
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index c545c95c..12d42924 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -25,7 +25,7 @@
android:layout_height="56dp"
android:contentInsetStart="0dp"
android:contentInsetStartWithNavigation="0dp"
- android:title="DirtyFrag"
+ android:title="@string/app_name"
android:titleMarginStart="32dp"
android:titleTextAppearance="@style/ToolbarTitle">
@@ -38,7 +38,7 @@
android:layout_gravity="end|center_vertical"
android:layout_marginEnd="16dp"
android:background="@drawable/bg_circle"
- android:contentDescription="Menu"
+ android:contentDescription="@string/menu"
android:scaleType="center"
android:src="@drawable/ic_more" />
@@ -67,7 +67,7 @@
android:layout_marginStart="32dp"
android:background="@drawable/bg_update_pill"
android:clickable="true"
- android:contentDescription="Update available, open the release page"
+ android:contentDescription="@string/update_available_description"
android:focusable="true"
android:visibility="gone">
@@ -80,7 +80,7 @@
android:layout_height="match_parent"
android:layout_gravity="start|center_vertical"
android:gravity="center"
- android:text="Update"
+ android:text="@string/update"
android:textColor="#FF101418"
android:textSize="16sp"
android:textStyle="bold" />
@@ -117,7 +117,7 @@
android:layout_marginEnd="6dp"
android:background="@drawable/bg_odin_pill"
android:clickable="true"
- android:contentDescription="Recovery mode: toggle download-mode authorisation"
+ android:contentDescription="@string/recovery_description"
android:focusable="true"
android:gravity="center_vertical"
android:minWidth="44dp"
@@ -137,7 +137,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
- android:text="Recovery"
+ android:text="@string/recovery"
android:textColor="#FFD9D9D9"
android:textSize="12sp"
android:textStyle="bold" />
@@ -223,7 +223,7 @@
@@ -231,7 +231,7 @@
android:id="@+id/suManagerSubtitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:text="Not selected - required"
+ android:text="@string/su_manager_required"
android:textColor="#FF9E9E9E"
android:textSize="12sp" />
@@ -242,7 +242,7 @@
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_gravity="center_vertical"
- android:contentDescription="Select"
+ android:contentDescription="@string/select"
android:src="@drawable/ic_chevron" />
@@ -285,7 +285,7 @@
android:id="@+id/tvAutorunTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:text="Autorun"
+ android:text="@string/autorun"
android:textColor="#FFFFFFFF"
android:textSize="16sp" />
@@ -293,7 +293,7 @@
android:id="@+id/tvAutorunDesc"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:text="Runs the exploit after boot"
+ android:text="@string/autorun_description"
android:textColor="#FF9E9E9E"
android:textSize="12sp" />
@@ -325,7 +325,7 @@
android:id="@+id/tvRebootTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:text="Auto reboot"
+ android:text="@string/auto_reboot"
android:textColor="#FFFFFFFF"
android:textSize="16sp" />
@@ -333,7 +333,7 @@
android:id="@+id/tvRebootDesc"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:text="Soft reboots after root"
+ android:text="@string/auto_reboot_description"
android:textColor="#FF9E9E9E"
android:textSize="12sp" />
@@ -381,7 +381,7 @@
@@ -389,7 +389,7 @@
android:id="@+id/modulesSubtitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:text="Enabled - active at next reboot"
+ android:text="@string/modules_enabled"
android:textColor="#FF9E9E9E"
android:textSize="12sp" />
@@ -436,14 +436,14 @@
@@ -545,7 +545,7 @@
android:paddingEnd="28dp"
android:elevation="12dp"
android:stateListAnimator="@null"
- android:text="Run exploit"
+ android:text="@string/run_exploit"
android:textSize="14sp"
android:backgroundTint="#FF7A7A7A"
android:textColor="#FFE0E0E0"
@@ -572,7 +572,7 @@
android:background="@drawable/bg_pill"
android:backgroundTint="#FF2E2E30"
android:clickable="true"
- android:contentDescription="Open SU manager"
+ android:contentDescription="@string/open_su_manager"
android:elevation="12dp"
android:enabled="false"
android:focusable="true"
@@ -590,7 +590,7 @@
android:layout_gravity="start|center_vertical"
android:layout_marginStart="22dp"
android:alpha="0"
- android:text="Manager"
+ android:text="@string/manager"
android:textColor="#FF1F1F1F"
android:textSize="14sp" />
@@ -600,7 +600,7 @@
android:layout_height="20dp"
android:layout_gravity="end|center_vertical"
android:layout_marginEnd="17dp"
- android:contentDescription="Open"
+ android:contentDescription="@string/open"
android:src="@drawable/ic_chevron" />
@@ -630,7 +630,7 @@
android:layout_alignBottom="@id/btnRun"
android:layout_marginStart="33dp"
android:background="@drawable/bg_circle_grey_ripple"
- android:contentDescription="Save log to Downloads"
+ android:contentDescription="@string/save_log"
android:elevation="12dp"
android:padding="0dp"
android:scaleType="center"
diff --git a/app/src/main/res/values-b+zh+Hans/strings.xml b/app/src/main/res/values-b+zh+Hans/strings.xml
new file mode 100644
index 00000000..c63e5649
--- /dev/null
+++ b/app/src/main/res/values-b+zh+Hans/strings.xml
@@ -0,0 +1,98 @@
+
+
+
+ 菜单
+ 更新
+ 有可用更新,打开发布页面
+ 打开 GitHub 项目
+ 重置界面布局
+ 专家模式
+ 隐藏卡片
+
+
+ Root 管理器
+ 请选择管理器
+ %1$s(%2$s)
+ 未找到包含 libksud.so 的应用
+ 选择
+ 开机自动提权
+ 开机自动提权(需专家模式)
+ 设备开机后自动尝试获取 Root
+ 自动软重启
+ 自动软重启(需专家模式)
+ 提权成功后执行软重启
+ KernelSU 模块
+ 已启用,下次提权时加载模块
+ 已禁用,下次提权时跳过模块
+ 显示高级日志
+ 查看可滚动的完整日志
+
+
+ 开始提权
+ 确认提权
+ 提权中
+ 已提权
+ 管理器
+ 打开 Root 管理器
+ 打开
+ 将日志保存到下载目录
+ 等待开始
+ 验证
+ 已验证
+ 提权失败
+ 请重启
+ %1$d%%
+
+
+ 下载模式
+ 打开下载模式授权菜单
+ 下载/恢复模式
+ 锁屏凭据
+ 维护模式
+ 设备支持
+ 写入下载授权
+ 自动写入授权
+ %1$s(再次提权时)
+ %1$s(此固件不适用)
+ %1$s(需专家模式)
+ 正在写入授权…
+ 正在清除授权…
+ 此固件无此限制
+ D2 已限制
+ 允许进入
+ 状态未知
+ 支持
+ 不支持
+ 已设置
+ 未设置
+ 已开启
+ 已关闭
+
+
+ 系统信息
+ 准备
+ 修补文件
+ 初始化
+ 清理
+ 提权成功
+ 提权失败:%1$s
+ 应用版本:%1$s
+ 制造商:%1$s
+ 设备型号:%1$s
+ Android 版本:%1$s
+ 安全补丁:%1$s
+ 内核版本:%1$s
+ 已检测到提权挂钩,跳过本次运行
+ 开发者选项中的「日志缓冲区大小」已关闭。
+ 请选择一个日志缓冲区大小,再重试。
+ 尚未选择 Root 管理器
+ %1$s 已卸载
+ 在 %1$s 中找不到 libksud.so
+ 修补检查显示设备不受此漏洞影响
+ 异常:%1$s
+ 读取应用设置失败
+ 停用模块失败
+ ksud 退出并返回错误
+ 请检查 logcat 和 dmesg 日志
+ 修补文件失败
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
new file mode 100644
index 00000000..91441ea7
--- /dev/null
+++ b/app/src/main/res/values/strings.xml
@@ -0,0 +1,100 @@
+
+
+ DirtyFrag
+
+
+ Menu
+ Update
+ Update available, open the release page
+ Open GitHub Page
+ Reset UI Layout
+ Expert Mode
+ Hide card
+
+
+ SU Manager
+ Not selected - required
+ "%1$s (%2$s)"
+ No app with libksud.so found
+ Select
+ Autorun
+ Autorun (Expert)
+ Runs the exploit after boot
+ Auto reboot
+ Auto reboot (Expert)
+ Soft reboots after root
+ KernelSU Modules
+ Enabled - Active at next reroot
+ Disabled - No Modules on reroot
+ Show advanced log
+ Shows full scrollable log
+
+
+ Run exploit
+ Are you sure
+ Running
+ Rooted
+ Manager
+ Open SU manager
+ Open
+ Save log to Downloads
+ Waiting
+ Verification
+ Verified
+ Failure
+ Reboot
+ %1$d%%
+
+
+ Recovery
+ Recovery mode: toggle download-mode authorisation
+ DL/Recovery
+ Lockscreen
+ Maintenance Mode
+ Device
+ Recovery Writer
+ Autorun Writer
+ %1$s (at reroot)
+ %1$s (no gate)
+ %1$s (Expert)
+ Writing flag ...
+ Clearing flag ...
+ Not gated here
+ D2 Blocked
+ Allowed
+ Unknown
+ Supported
+ Unsupported
+ Yes
+ No
+ On
+ Off
+
+
+ SYSTEM
+ SETUP
+ PATCHING
+ INIT
+ CLEANUP
+ EXPLOIT SUCCESS
+ EXPLOIT FAILED: %1$s
+ app version: %1$s
+ manufacturer: %1$s
+ model: %1$s
+ android version: %1$s
+ security patch: %1$s
+ kernel version: %1$s
+ already hooked, skipping
+ ERROR: Developer Options → \'Log buffer size\' is set to Off.
+ Change it to any other value and retry.
+ no SU manager selected
+ %1$s is no longer installed
+ libksud.so not found in %1$s
+ patch: DEVICE NOT VULNERABLE
+ exception: %1$s
+ READING PREFS FAILED
+ DISABLE MODULES FAILED
+ KSUD NONZERO EXIT
+ CHECK LOGCAT & DMESG
+ FAILED TO PATCH FILES
+
diff --git a/app/src/main/res/xml/locales_config.xml b/app/src/main/res/xml/locales_config.xml
new file mode 100644
index 00000000..768c5699
--- /dev/null
+++ b/app/src/main/res/xml/locales_config.xml
@@ -0,0 +1,5 @@
+
+
+
+
+