Auto-translate app text with a local-first pipeline: local cache → on-device ML Kit (local server) → Gemini API → Free Google API.
- Translation speed improvements by shifting language-pair handling to the local server and reducing hook-side work.
- Refactored into a multi-module, multi-APK setup: full / hook-only / server-only.
- Supports unroot environments (tested with NPatch v0.7.3): patch target app with hook-only + install server-only.
- Prioritize on-device translation via a built-in local server using Google ML Kit.
- Added translation support for android.text.StaticLayout$Builder to reduce UI jank by replacing text synchronously when possible.
Requirements:
- LSPosed (or another Xposed framework) installed and enabled.
Steps:
- Install the full APK (
app). - Open the LSPosed manager, enable XPTranslateText, and select your target apps.
- Open the app, enable the "Local Translation Server" switch, and configure settings.
- Force stop the target app and relaunch.
Tested with:
- NPatch v0.7.3
Steps:
- Patch the target app with
app-hook(hook-only APK). - Install
app-server(server-only APK). - Open
app-serverand start the local translation server (manual start). - Launch the patched target app.
- Android 13 with LSPosed (v1.9.2-it(7024))
- Android 15 with LSPosed (v1.9.2-it(7024))
- Android 16 with LSPosed (v1.9.2-it(7412))
- Unroot: NPatch v0.7.3 (hook-only + server-only)
| Before | After |
|---|---|
![]() |
![]() |
XPTranslateText/
├── app/ # full APK (hook + server)
├── app-hook/ # hook-only APK (for NPatch/LSPatch patching)
├── app-server/ # server-only APK (manual start)
├── core-hook/ # hook entry + client (no UI)
├── core-server/ # local server + UI + resources
├── app/libs/ # Xposed/Android API stubs (compileOnly)
├── gradlew
├── gradlew.bat
├── keystore.properties # Local-only, not committed
└── settings.gradle
app(full): For normal root / LSPosed usage (one APK includes hook + local server + UI).app-hook(hook-only): For patchers like NPatch/LSPatch to embed the hook into a target app (no UI / no server).app-server(server-only): Standalone local translation server app (manually start/stop).
- full Debug:
./gradlew :app:assembleDebug - hook-only Debug:
./gradlew :app-hook:assembleDebug - server-only Debug:
./gradlew :app-server:assembleDebug - Requires Java 17 (macOS:
export JAVA_HOME=$(/usr/libexec/java_home -v 17))
- Runs as a foreground service on
127.0.0.1:18181. - Endpoint:
/translate?q=...(src/dstare optional)- If
src/dstare omitted, the server uses saved settings fromxp_translate_text_configs. src=autoenables automatic language detection (ML Kit Language ID).
- If
- Endpoint:
/config- Returns JSON:
{"code":0,"source_lang":"auto","target_lang":"zh-TW","force_wait_local":false} core-hookreads this to avoidXSharedPreferences(works for both root and unroot / NPatch scenarios).- Hook-side config cache TTL: 1s; when language pair changes, the in-memory translation cache is cleared.
- Returns JSON:
- Models are downloaded on-demand and kept on-device; last-used times are tracked to help with maintenance.
-
android.widget.TextView & Custom Components:
- Automatically translates text set via the
setText()method. - Translation pipeline:
- Local Translation Cache (memory + SQLite) to avoid repeated work.
- Local ML Kit Server (on-device, 127.0.0.1:18181) with short timeouts to keep UI responsive.
- Gemini API (gemini-2.0-flash-lite) as network fallback.
- Free Google API as the final fallback.
- Automatically translates text set via the
-
android.text.StaticLayout$Builder:
- Intercepts
StaticLayout.Builder.build()and attempts a synchronous replacement when translations are readily available (cache/DB) or quickly obtained from the local ML Kit server. - If not immediately available, it returns the original layout and prefetches translations asynchronously for subsequent renders.
- Spans and formatting are preserved.
- Intercepts
-
android.webkit.WebView:
- Performs real-time translation of visible webpage text via a JS bridge.
- Pipeline: Local ML Kit Server → Gemini API → Free Google API (no caching for WebView).


