android-localisation 1.0.7__tar.gz → 1.1.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {android_localisation-1.0.7/android_localisation.egg-info → android_localisation-1.1.2}/PKG-INFO +79 -28
- {android_localisation-1.0.7 → android_localisation-1.1.2}/README.md +78 -27
- {android_localisation-1.0.7 → android_localisation-1.1.2}/android_localisation/__init__.py +1 -1
- {android_localisation-1.0.7 → android_localisation-1.1.2}/android_localisation/cli.py +24 -8
- {android_localisation-1.0.7 → android_localisation-1.1.2}/android_localisation/fix.py +39 -17
- android_localisation-1.1.2/android_localisation/java/VerifyStrings.java +138 -0
- android_localisation-1.1.2/android_localisation/resources.py +214 -0
- {android_localisation-1.0.7 → android_localisation-1.1.2}/android_localisation/translate.py +117 -72
- android_localisation-1.1.2/android_localisation/updates.py +95 -0
- android_localisation-1.1.2/android_localisation/verify.py +82 -0
- {android_localisation-1.0.7 → android_localisation-1.1.2/android_localisation.egg-info}/PKG-INFO +79 -28
- {android_localisation-1.0.7 → android_localisation-1.1.2}/android_localisation.egg-info/SOURCES.txt +2 -0
- {android_localisation-1.0.7 → android_localisation-1.1.2}/pyproject.toml +1 -1
- android_localisation-1.0.7/android_localisation/java/VerifyStrings.java +0 -219
- android_localisation-1.0.7/android_localisation/verify.py +0 -56
- {android_localisation-1.0.7 → android_localisation-1.1.2}/LICENSE +0 -0
- {android_localisation-1.0.7 → android_localisation-1.1.2}/MANIFEST.in +0 -0
- {android_localisation-1.0.7 → android_localisation-1.1.2}/android_localisation.egg-info/dependency_links.txt +0 -0
- {android_localisation-1.0.7 → android_localisation-1.1.2}/android_localisation.egg-info/entry_points.txt +0 -0
- {android_localisation-1.0.7 → android_localisation-1.1.2}/android_localisation.egg-info/top_level.txt +0 -0
- {android_localisation-1.0.7 → android_localisation-1.1.2}/setup.cfg +0 -0
{android_localisation-1.0.7/android_localisation.egg-info → android_localisation-1.1.2}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: android-localisation
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.1.2
|
|
4
4
|
Summary: Zero-dependency Android strings.xml translation and verification using LLMs (Gemini, OpenAI, Anthropic, Ollama).
|
|
5
5
|
License: MIT
|
|
6
6
|
Project-URL: Homepage, https://github.com/BharathKmalviya/android-llm-localization
|
|
@@ -54,6 +54,26 @@ pip install android-localisation
|
|
|
54
54
|
|
|
55
55
|
Requires Python 3.8+. No other dependencies.
|
|
56
56
|
|
|
57
|
+
CLI output uses UTF-8, including when redirected to a file or pipe on Windows.
|
|
58
|
+
|
|
59
|
+
### Update notices
|
|
60
|
+
|
|
61
|
+
Interactive `android-localise` commands check PyPI for a newer stable release in
|
|
62
|
+
the background, at most once every 24 hours. When available, a note on stderr
|
|
63
|
+
shows `pip install --upgrade android-localisation`. The CLI never waits for the
|
|
64
|
+
lookup or installs an update automatically. A first short command may finish
|
|
65
|
+
before the lookup completes; a notice appears when a check completes during a
|
|
66
|
+
command or a later invocation can use its cached result.
|
|
67
|
+
|
|
68
|
+
Checks are skipped for help/version output, CI (`CI` set), redirected output and
|
|
69
|
+
pipes. Set `ANDROID_LOCALISE_NO_UPDATE_CHECK=1` to disable them, including when
|
|
70
|
+
using a local model offline. Lookup or cache failures stay silent and do not
|
|
71
|
+
change command exit codes. The small cache lives under
|
|
72
|
+
`%LOCALAPPDATA%/android-localisation` on Windows, or
|
|
73
|
+
`${XDG_CACHE_HOME:-~/.cache}/android-localisation` elsewhere. Only the package's
|
|
74
|
+
public release metadata is requested; API keys and Android resources are never
|
|
75
|
+
included.
|
|
76
|
+
|
|
57
77
|
---
|
|
58
78
|
|
|
59
79
|
## Quick start
|
|
@@ -65,7 +85,7 @@ android-localise translate --api-key YOUR_GEMINI_KEY
|
|
|
65
85
|
# Step 2 — fix any formatting issues the LLM may have introduced
|
|
66
86
|
android-localise fix
|
|
67
87
|
|
|
68
|
-
# Step 3 —
|
|
88
|
+
# Step 3 — check resources and Java formatting
|
|
69
89
|
android-localise verify
|
|
70
90
|
```
|
|
71
91
|
|
|
@@ -78,9 +98,9 @@ That's the full workflow. Run these three commands after every time you update y
|
|
|
78
98
|
When you run `android-localise translate --api-key YOUR_KEY`, here's exactly what it does:
|
|
79
99
|
|
|
80
100
|
1. Looks for `app/src/main/res/values/strings.xml` — this is your English source
|
|
81
|
-
2. If `--languages` is provided,
|
|
82
|
-
3.
|
|
83
|
-
4.
|
|
101
|
+
2. If `--languages` is provided, selects those locales. Otherwise scans existing locale folders, skipping configuration-only folders such as `values-night`, `values-land`, `values-car`, and `values-sw600dp`
|
|
102
|
+
3. Sends your English XML to the LLM with app context and instructions to preserve resource structure, protected values, namespaces and format specifiers. With `--missing-only`, requests only resources absent from the target file; existing resources remain untouched, and complete locales make no API request
|
|
103
|
+
4. Parses the response and checks duplicate/unexpected/missing resources, attributes, inline markup, item structure, control escapes and format arguments. A valid result replaces the file atomically; new folders are created only when saving. `--dry-run` shows a diff without writing any files or creating folders
|
|
84
104
|
5. Waits 5 seconds between each language request to avoid hitting API rate limits
|
|
85
105
|
|
|
86
106
|
**Defaults used when you don't specify anything:**
|
|
@@ -88,12 +108,14 @@ When you run `android-localise translate --api-key YOUR_KEY`, here's exactly wha
|
|
|
88
108
|
| What | Default |
|
|
89
109
|
|---|---|
|
|
90
110
|
| Provider | Gemini |
|
|
91
|
-
| Model | `gemini-3.
|
|
111
|
+
| Model | `gemini-3.8-flash` |
|
|
92
112
|
| Source directory | `app/src/main/res` |
|
|
93
113
|
| Delay between requests | 5 seconds |
|
|
94
114
|
| App context | none (generic prompt) |
|
|
95
115
|
|
|
96
|
-
|
|
116
|
+
An invalid or incomplete response leaves the existing file unchanged. Other locales continue, and the final summary shows succeeded, failed and skipped counts. Exit code **0** means success; **1** means a setup, validation, API or save failure (including partial failure). Argument syntax errors use argparse's exit code **2**.
|
|
117
|
+
|
|
118
|
+
Normal translation still replaces the whole locale file. Use `--missing-only` to retain reviewed translations or combine it with `--dry-run` to preview additions. No cache, database or configuration file is needed.
|
|
97
119
|
|
|
98
120
|
---
|
|
99
121
|
|
|
@@ -107,7 +129,7 @@ For target languages, you have two options:
|
|
|
107
129
|
```bash
|
|
108
130
|
android-localise translate --api-key YOUR_KEY --languages hi,es,fr,de
|
|
109
131
|
```
|
|
110
|
-
This
|
|
132
|
+
This translates into Hindi, Spanish, French and German, creating each folder and `strings.xml` when its translation passes validation.
|
|
111
133
|
|
|
112
134
|
**Option B — pre-create folders yourself:**
|
|
113
135
|
```
|
|
@@ -118,7 +140,7 @@ app/src/main/res/
|
|
|
118
140
|
├── values-es/
|
|
119
141
|
└── values-fr/
|
|
120
142
|
```
|
|
121
|
-
Run `android-localise translate --api-key YOUR_KEY` and it picks up
|
|
143
|
+
Run `android-localise translate --api-key YOUR_KEY` and it picks up locale folders, creating `strings.xml` inside each one after successful translation. Locale examples include `values-hi`, `values-es-rES`, `values-b+zh+Hans`, and `values-en-night`; qualifier-only folders are skipped. `--languages` accepts the same forms without the optional `values-` prefix and rejects path separators or non-locale names.
|
|
122
144
|
|
|
123
145
|
**Get a free API key:** [Google Gemini AI Studio](https://aistudio.google.com/) → Get API Key. The free tier handles most apps without hitting limits.
|
|
124
146
|
|
|
@@ -153,6 +175,19 @@ android-localise translate \
|
|
|
153
175
|
| `--base-url` | API endpoint for local/custom providers | — |
|
|
154
176
|
| `--sleep` | Seconds to wait between language requests | `5.0` |
|
|
155
177
|
| `--timeout` | Seconds to wait for each API response (up to 3 attempts on timeout) | `180` |
|
|
178
|
+
| `--missing-only` | Translate absent resources; retain existing translations | off (refresh whole file) |
|
|
179
|
+
| `--dry-run` | Generate and validate output, then print a diff without writing files | off |
|
|
180
|
+
|
|
181
|
+
**Preserve reviewed translations:**
|
|
182
|
+
|
|
183
|
+
```bash
|
|
184
|
+
android-localise translate --languages hi,es --missing-only
|
|
185
|
+
android-localise translate --languages hi --missing-only --dry-run
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
`--dry-run` still calls the selected provider and may incur API charges. It is a translation preview, not a no-network estimate. `--missing-only` keeps existing resources, comments and text, but validates them against the current source first. An invalid existing file is reported rather than silently repaired. It does not detect changed English text for an existing key; use the normal translation mode when you deliberately want to refresh those resources. Arrays and plurals count as whole resources: this mode does not fill individual missing items.
|
|
189
|
+
|
|
190
|
+
Validation permits positional placeholder reordering such as `%s %d` becoming `%2$d %1$s`, while checking argument identities, conversions, formatting options and occurrence counts. Non-translatable resources may be omitted from locale files to use Android's default fallback, but must remain unchanged if included.
|
|
156
191
|
|
|
157
192
|
---
|
|
158
193
|
|
|
@@ -163,12 +198,14 @@ android-localise fix
|
|
|
163
198
|
android-localise fix --res-dir path/to/res
|
|
164
199
|
```
|
|
165
200
|
|
|
166
|
-
|
|
201
|
+
This command scans locale `strings.xml` files, converts curly apostrophes, escapes raw apostrophes, and doubles bare percent signs while retaining recognized format patterns. It checks XML before and after changes and saves atomically. Inline markup attributes and non-translatable values are left alone. It handles single- and double-quoted resource names.
|
|
167
202
|
|
|
168
203
|
Strings marked `formatted="false"` are skipped — their `%` signs are literal, not format specifiers.
|
|
169
204
|
|
|
170
205
|
Always run this before `verify` and before building.
|
|
171
206
|
|
|
207
|
+
`fix` repairs `<string>` text only. It does not repair arbitrary malformed XML, double-quote handling, or array/plural items, and it does not prove a format pattern is valid. Review its changes and use `verify` and your Android build afterward.
|
|
208
|
+
|
|
172
209
|
---
|
|
173
210
|
|
|
174
211
|
### `verify`
|
|
@@ -178,10 +215,12 @@ android-localise verify
|
|
|
178
215
|
android-localise verify --res-dir path/to/res
|
|
179
216
|
```
|
|
180
217
|
|
|
181
|
-
|
|
218
|
+
First parses XML and compares localized resources with `values/strings.xml`, checking coverage, duplicates, protected content, attributes, inline markup and format-argument preservation. Then checks formatted strings and array/plural items with Java's actual `String.format()` runtime, including date/time and relative argument indexing. Both checks return nonzero on failure. Empty locale folders without a `strings.xml` are skipped.
|
|
182
219
|
|
|
183
220
|
Strings marked `formatted="false"` are skipped. Requires `javac` in your PATH. If you don't have it system-wide, run this from the Terminal tab inside Android Studio — it ships with a JDK.
|
|
184
221
|
|
|
222
|
+
`formatted="false"` skips formatting checks only; XML, resource coverage and attribute checks still apply. Java compilation uses a temporary directory, so installed package files are not modified. Verification is deliberately stricter than Android's missing-string fallback: missing translatable resources are reported. Passing these checks does not replace an Android build, native-speaker review or device layout checks.
|
|
223
|
+
|
|
185
224
|
---
|
|
186
225
|
|
|
187
226
|
### `models`
|
|
@@ -191,27 +230,33 @@ android-localise models # all providers
|
|
|
191
230
|
android-localise models --provider openai # one provider
|
|
192
231
|
```
|
|
193
232
|
|
|
194
|
-
Lists
|
|
233
|
+
Lists this CLI's configured defaults and automatic fallbacks, not the provider's entire model catalog. Any supported model can still be selected with `--model`.
|
|
195
234
|
|
|
196
235
|
---
|
|
197
236
|
|
|
198
237
|
## Providers
|
|
199
238
|
|
|
200
|
-
By default the tool uses Gemini with `gemini-3.
|
|
239
|
+
By default the tool uses Gemini with `gemini-3.8-flash`. You can switch providers with `--provider` and optionally pin a specific model with `--model`. Configured defaults and fallbacks use only the latest general-purpose text-model lineup, with defaults favoring speed and cost within that lineup.
|
|
201
240
|
|
|
202
241
|
| Provider | Default model | Fallbacks | API key env var |
|
|
203
242
|
|---|---|---|---|
|
|
204
|
-
| `gemini` _(default)_ | `gemini-3.
|
|
205
|
-
| `openai` | `gpt-
|
|
206
|
-
| `anthropic` | `claude-
|
|
243
|
+
| `gemini` _(default)_ | `gemini-3.8-flash` | none | `GEMINI_API_KEY` |
|
|
244
|
+
| `openai` | `gpt-6-luna` | `gpt-6.1-sol` → `gpt-6-astra` | `OPENAI_API_KEY` |
|
|
245
|
+
| `anthropic` | `claude-sonnet-5-5` | `claude-opus-5-5` | `ANTHROPIC_API_KEY` |
|
|
207
246
|
| `custom` | set with `--model` | none | `OPENAI_API_KEY` (optional) |
|
|
208
247
|
|
|
209
248
|
If the default model returns a "model not found" error (e.g. it was deprecated), the tool automatically retries with the next fallback. If you pin a model with `--model`, no fallback is used.
|
|
210
249
|
|
|
250
|
+
Model IDs and compatibility checked against [Google's model catalog](https://ai.google.dev/gemini-api/docs/models), [OpenAI's model catalog](https://developers.openai.com/api/docs/models) and [Anthropic's model catalog](https://platform.claude.com/docs/en/models/overview) on **2026-10-02**. Older Gemini, GPT-5 and Haiku 4.5 models are excluded from automatic selection. Gemini has no fallback in its latest stable text generation. OpenAI and Anthropic fallbacks are higher-cost models; pin `--model` to avoid automatic tier changes. Explicit model selection and custom/local endpoints remain available. The catalog is bundled with each CLI release, rather than automatically discovering models at runtime.
|
|
251
|
+
|
|
252
|
+
OpenAI and local providers retain the Chat Completions request format. Provider replies indicating truncation or blocked/incomplete output are rejected. Anthropic allows up to 16,384 output tokens per request and text blocks are collected separately from thinking blocks. Large files can still exceed a model's limits; automatic batching is future work.
|
|
253
|
+
|
|
211
254
|
**Using OpenAI:**
|
|
212
255
|
```bash
|
|
213
256
|
android-localise translate --provider openai --api-key YOUR_KEY
|
|
214
|
-
android-localise translate --provider openai --model gpt-
|
|
257
|
+
android-localise translate --provider openai --model gpt-6-luna --api-key YOUR_KEY
|
|
258
|
+
# Optional stronger tier; different cost/latency
|
|
259
|
+
android-localise translate --provider openai --model gpt-6.1-sol --api-key YOUR_KEY
|
|
215
260
|
```
|
|
216
261
|
|
|
217
262
|
**Using Anthropic:**
|
|
@@ -262,6 +307,8 @@ android-localise translate
|
|
|
262
307
|
| `OPENAI_API_KEY` | `--provider openai` and `--provider custom` |
|
|
263
308
|
| `ANTHROPIC_API_KEY` | `--provider anthropic` |
|
|
264
309
|
| `API_KEY` | fallback for any provider if the provider-specific var is not set |
|
|
310
|
+
| `ANDROID_LOCALISE_NO_UPDATE_CHECK` | Set to `1` to disable update checks/notices |
|
|
311
|
+
| `CI` | When nonempty, skips update checks/notices |
|
|
265
312
|
|
|
266
313
|
---
|
|
267
314
|
|
|
@@ -290,15 +337,15 @@ android-localise verify
|
|
|
290
337
|
./gradlew assembleDebug
|
|
291
338
|
```
|
|
292
339
|
|
|
293
|
-
After this, whenever you add or change strings in your English `strings.xml`, run the same three commands again. Existing translated strings
|
|
340
|
+
After this, whenever you add or change strings in your English `strings.xml`, run the same three commands again. Existing translated strings are refreshed by default. For additions that should retain existing translations, run `translate --missing-only` instead; it does not detect changed source text for existing keys.
|
|
294
341
|
|
|
295
342
|
---
|
|
296
343
|
|
|
297
344
|
## Platform support
|
|
298
345
|
|
|
299
|
-
|
|
346
|
+
I develop and **manually test this project on Windows only** at the moment. It is written in pure Python (stdlib only) and should run on macOS and Linux, but I have **not verified** those platforms yet.
|
|
300
347
|
|
|
301
|
-
|
|
348
|
+
I especially need help testing on:
|
|
302
349
|
|
|
303
350
|
- **macOS** — `translate`, `fix`, `verify` (including `javac` / Android Studio terminal)
|
|
304
351
|
- **Linux** — same workflow, plus common CI environments
|
|
@@ -309,7 +356,7 @@ If you use another OS, please try the [quick start](#quick-start) workflow and r
|
|
|
309
356
|
- **Broken?** — open a [bug report](https://github.com/BharathKmalviya/android-llm-localization/issues/new?template=bug_report.md) with the full error output
|
|
310
357
|
- **Want to help more?** — see [Contributing](#contributing) and [CONTRIBUTING.md](CONTRIBUTING.md)
|
|
311
358
|
|
|
312
|
-
|
|
359
|
+
PRs with cross-platform fixes and test notes are especially appreciated.
|
|
313
360
|
|
|
314
361
|
---
|
|
315
362
|
|
|
@@ -317,10 +364,13 @@ Cross-platform fixes and test notes in pull requests are very welcome.
|
|
|
317
364
|
|
|
318
365
|
| Topic | Detail |
|
|
319
366
|
|---|---|
|
|
320
|
-
| **Platform testing** |
|
|
321
|
-
| **Scope** |
|
|
322
|
-
| **
|
|
323
|
-
| **
|
|
367
|
+
| **Platform testing** | I test on **Windows only** — macOS and Linux need community verification (see [Platform support](#platform-support)) |
|
|
368
|
+
| **Scope** | Reads `values/strings.xml` only. Strings, arrays and plurals within that file are checked; separate XML files are not scanned |
|
|
369
|
+
| **Plurals** | Preserves source quantities and item structure; does not generate target-language plural categories. Review plural completeness for each language |
|
|
370
|
+
| **Overwrite** | Normal runs refresh whole files. `--missing-only` retains existing resources but does not detect source changes |
|
|
371
|
+
| **Folder scan** | Recognizes language-first and Android `b+` locale forms, with optional trailing qualifiers. MCC/MNC-prefixed resource folders are not scanned |
|
|
372
|
+
| **Validation** | Requires source attributes, inline element order and formatting options to match. DTD/entity declarations are unsupported; checks do not replace Android compilation or language review |
|
|
373
|
+
| **Large files** | One request per locale; no automatic batching or resume cache. Incomplete output is rejected |
|
|
324
374
|
| **Network** | `translate` requires internet access to reach the LLM API (except local `custom` providers) |
|
|
325
375
|
| **JDK** | `verify` requires `javac` on your PATH |
|
|
326
376
|
|
|
@@ -333,6 +383,7 @@ Cross-platform fixes and test notes in pull requests are very welcome.
|
|
|
333
383
|
| `Could not find English strings.xml` | Check `--res-dir` points to your `res/` folder and `values/strings.xml` exists |
|
|
334
384
|
| `No locale directories found` | Add `--languages hi,es,fr` or create `values-<lang>/` folders manually |
|
|
335
385
|
| API auth errors | Confirm your key env var or `--api-key` matches the `--provider` |
|
|
386
|
+
| Resource validation fails | Read the named resource error; check source/target keys, attributes, placeholders and markup. Existing files are retained |
|
|
336
387
|
| `javac` not found | Install a JDK or run `verify` from Android Studio's terminal |
|
|
337
388
|
| Build fails on apostrophes | Run `android-localise fix` before building |
|
|
338
389
|
| `%` crashes at runtime | Run `android-localise verify` — it catches bad format specifiers before release |
|
|
@@ -345,17 +396,17 @@ Cross-platform fixes and test notes in pull requests are very welcome.
|
|
|
345
396
|
- [ ] **iOS support** — translate `Localizable.strings` and `Localizable.xcstrings` for iOS/macOS apps. The LLM prompt and provider logic is already in place — it mainly needs a parser for Apple's strings format and the right folder structure (`<lang>.lproj/`). Good first contribution if you're familiar with iOS projects.
|
|
346
397
|
- [ ] **Smarter locale folder detection** — skip non-locale `values-*` qualifiers (`night`, `sw600dp`, `v21`, etc.) when scanning without `--languages`
|
|
347
398
|
- [ ] **Automated test suite** — unit tests for `fix`, XML parsing, and format-specifier edge cases
|
|
348
|
-
- [ ] **Cross-platform verification** — confirm `translate`, `fix`, and `verify` on macOS and Linux (
|
|
399
|
+
- [ ] **Cross-platform verification** — confirm `translate`, `fix`, and `verify` on macOS and Linux (I currently test on Windows only)
|
|
349
400
|
|
|
350
401
|
---
|
|
351
402
|
|
|
352
403
|
## Contributing
|
|
353
404
|
|
|
354
|
-
|
|
405
|
+
I welcome bug reports, pull requests, and **cross-platform testing**. For larger changes, please open an issue first.
|
|
355
406
|
|
|
356
407
|
**No code required** — if you are on macOS or Linux, running the tool and filing an issue (pass or fail) is a real contribution. See [Platform support](#platform-support).
|
|
357
408
|
|
|
358
|
-
See [CONTRIBUTING.md](CONTRIBUTING.md) for the
|
|
409
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for the development workflow, branch strategy, and release process.
|
|
359
410
|
|
|
360
411
|
```bash
|
|
361
412
|
git clone https://github.com/BharathKmalviya/android-llm-localization
|
|
@@ -26,6 +26,26 @@ pip install android-localisation
|
|
|
26
26
|
|
|
27
27
|
Requires Python 3.8+. No other dependencies.
|
|
28
28
|
|
|
29
|
+
CLI output uses UTF-8, including when redirected to a file or pipe on Windows.
|
|
30
|
+
|
|
31
|
+
### Update notices
|
|
32
|
+
|
|
33
|
+
Interactive `android-localise` commands check PyPI for a newer stable release in
|
|
34
|
+
the background, at most once every 24 hours. When available, a note on stderr
|
|
35
|
+
shows `pip install --upgrade android-localisation`. The CLI never waits for the
|
|
36
|
+
lookup or installs an update automatically. A first short command may finish
|
|
37
|
+
before the lookup completes; a notice appears when a check completes during a
|
|
38
|
+
command or a later invocation can use its cached result.
|
|
39
|
+
|
|
40
|
+
Checks are skipped for help/version output, CI (`CI` set), redirected output and
|
|
41
|
+
pipes. Set `ANDROID_LOCALISE_NO_UPDATE_CHECK=1` to disable them, including when
|
|
42
|
+
using a local model offline. Lookup or cache failures stay silent and do not
|
|
43
|
+
change command exit codes. The small cache lives under
|
|
44
|
+
`%LOCALAPPDATA%/android-localisation` on Windows, or
|
|
45
|
+
`${XDG_CACHE_HOME:-~/.cache}/android-localisation` elsewhere. Only the package's
|
|
46
|
+
public release metadata is requested; API keys and Android resources are never
|
|
47
|
+
included.
|
|
48
|
+
|
|
29
49
|
---
|
|
30
50
|
|
|
31
51
|
## Quick start
|
|
@@ -37,7 +57,7 @@ android-localise translate --api-key YOUR_GEMINI_KEY
|
|
|
37
57
|
# Step 2 — fix any formatting issues the LLM may have introduced
|
|
38
58
|
android-localise fix
|
|
39
59
|
|
|
40
|
-
# Step 3 —
|
|
60
|
+
# Step 3 — check resources and Java formatting
|
|
41
61
|
android-localise verify
|
|
42
62
|
```
|
|
43
63
|
|
|
@@ -50,9 +70,9 @@ That's the full workflow. Run these three commands after every time you update y
|
|
|
50
70
|
When you run `android-localise translate --api-key YOUR_KEY`, here's exactly what it does:
|
|
51
71
|
|
|
52
72
|
1. Looks for `app/src/main/res/values/strings.xml` — this is your English source
|
|
53
|
-
2. If `--languages` is provided,
|
|
54
|
-
3.
|
|
55
|
-
4.
|
|
73
|
+
2. If `--languages` is provided, selects those locales. Otherwise scans existing locale folders, skipping configuration-only folders such as `values-night`, `values-land`, `values-car`, and `values-sw600dp`
|
|
74
|
+
3. Sends your English XML to the LLM with app context and instructions to preserve resource structure, protected values, namespaces and format specifiers. With `--missing-only`, requests only resources absent from the target file; existing resources remain untouched, and complete locales make no API request
|
|
75
|
+
4. Parses the response and checks duplicate/unexpected/missing resources, attributes, inline markup, item structure, control escapes and format arguments. A valid result replaces the file atomically; new folders are created only when saving. `--dry-run` shows a diff without writing any files or creating folders
|
|
56
76
|
5. Waits 5 seconds between each language request to avoid hitting API rate limits
|
|
57
77
|
|
|
58
78
|
**Defaults used when you don't specify anything:**
|
|
@@ -60,12 +80,14 @@ When you run `android-localise translate --api-key YOUR_KEY`, here's exactly wha
|
|
|
60
80
|
| What | Default |
|
|
61
81
|
|---|---|
|
|
62
82
|
| Provider | Gemini |
|
|
63
|
-
| Model | `gemini-3.
|
|
83
|
+
| Model | `gemini-3.8-flash` |
|
|
64
84
|
| Source directory | `app/src/main/res` |
|
|
65
85
|
| Delay between requests | 5 seconds |
|
|
66
86
|
| App context | none (generic prompt) |
|
|
67
87
|
|
|
68
|
-
|
|
88
|
+
An invalid or incomplete response leaves the existing file unchanged. Other locales continue, and the final summary shows succeeded, failed and skipped counts. Exit code **0** means success; **1** means a setup, validation, API or save failure (including partial failure). Argument syntax errors use argparse's exit code **2**.
|
|
89
|
+
|
|
90
|
+
Normal translation still replaces the whole locale file. Use `--missing-only` to retain reviewed translations or combine it with `--dry-run` to preview additions. No cache, database or configuration file is needed.
|
|
69
91
|
|
|
70
92
|
---
|
|
71
93
|
|
|
@@ -79,7 +101,7 @@ For target languages, you have two options:
|
|
|
79
101
|
```bash
|
|
80
102
|
android-localise translate --api-key YOUR_KEY --languages hi,es,fr,de
|
|
81
103
|
```
|
|
82
|
-
This
|
|
104
|
+
This translates into Hindi, Spanish, French and German, creating each folder and `strings.xml` when its translation passes validation.
|
|
83
105
|
|
|
84
106
|
**Option B — pre-create folders yourself:**
|
|
85
107
|
```
|
|
@@ -90,7 +112,7 @@ app/src/main/res/
|
|
|
90
112
|
├── values-es/
|
|
91
113
|
└── values-fr/
|
|
92
114
|
```
|
|
93
|
-
Run `android-localise translate --api-key YOUR_KEY` and it picks up
|
|
115
|
+
Run `android-localise translate --api-key YOUR_KEY` and it picks up locale folders, creating `strings.xml` inside each one after successful translation. Locale examples include `values-hi`, `values-es-rES`, `values-b+zh+Hans`, and `values-en-night`; qualifier-only folders are skipped. `--languages` accepts the same forms without the optional `values-` prefix and rejects path separators or non-locale names.
|
|
94
116
|
|
|
95
117
|
**Get a free API key:** [Google Gemini AI Studio](https://aistudio.google.com/) → Get API Key. The free tier handles most apps without hitting limits.
|
|
96
118
|
|
|
@@ -125,6 +147,19 @@ android-localise translate \
|
|
|
125
147
|
| `--base-url` | API endpoint for local/custom providers | — |
|
|
126
148
|
| `--sleep` | Seconds to wait between language requests | `5.0` |
|
|
127
149
|
| `--timeout` | Seconds to wait for each API response (up to 3 attempts on timeout) | `180` |
|
|
150
|
+
| `--missing-only` | Translate absent resources; retain existing translations | off (refresh whole file) |
|
|
151
|
+
| `--dry-run` | Generate and validate output, then print a diff without writing files | off |
|
|
152
|
+
|
|
153
|
+
**Preserve reviewed translations:**
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
android-localise translate --languages hi,es --missing-only
|
|
157
|
+
android-localise translate --languages hi --missing-only --dry-run
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
`--dry-run` still calls the selected provider and may incur API charges. It is a translation preview, not a no-network estimate. `--missing-only` keeps existing resources, comments and text, but validates them against the current source first. An invalid existing file is reported rather than silently repaired. It does not detect changed English text for an existing key; use the normal translation mode when you deliberately want to refresh those resources. Arrays and plurals count as whole resources: this mode does not fill individual missing items.
|
|
161
|
+
|
|
162
|
+
Validation permits positional placeholder reordering such as `%s %d` becoming `%2$d %1$s`, while checking argument identities, conversions, formatting options and occurrence counts. Non-translatable resources may be omitted from locale files to use Android's default fallback, but must remain unchanged if included.
|
|
128
163
|
|
|
129
164
|
---
|
|
130
165
|
|
|
@@ -135,12 +170,14 @@ android-localise fix
|
|
|
135
170
|
android-localise fix --res-dir path/to/res
|
|
136
171
|
```
|
|
137
172
|
|
|
138
|
-
|
|
173
|
+
This command scans locale `strings.xml` files, converts curly apostrophes, escapes raw apostrophes, and doubles bare percent signs while retaining recognized format patterns. It checks XML before and after changes and saves atomically. Inline markup attributes and non-translatable values are left alone. It handles single- and double-quoted resource names.
|
|
139
174
|
|
|
140
175
|
Strings marked `formatted="false"` are skipped — their `%` signs are literal, not format specifiers.
|
|
141
176
|
|
|
142
177
|
Always run this before `verify` and before building.
|
|
143
178
|
|
|
179
|
+
`fix` repairs `<string>` text only. It does not repair arbitrary malformed XML, double-quote handling, or array/plural items, and it does not prove a format pattern is valid. Review its changes and use `verify` and your Android build afterward.
|
|
180
|
+
|
|
144
181
|
---
|
|
145
182
|
|
|
146
183
|
### `verify`
|
|
@@ -150,10 +187,12 @@ android-localise verify
|
|
|
150
187
|
android-localise verify --res-dir path/to/res
|
|
151
188
|
```
|
|
152
189
|
|
|
153
|
-
|
|
190
|
+
First parses XML and compares localized resources with `values/strings.xml`, checking coverage, duplicates, protected content, attributes, inline markup and format-argument preservation. Then checks formatted strings and array/plural items with Java's actual `String.format()` runtime, including date/time and relative argument indexing. Both checks return nonzero on failure. Empty locale folders without a `strings.xml` are skipped.
|
|
154
191
|
|
|
155
192
|
Strings marked `formatted="false"` are skipped. Requires `javac` in your PATH. If you don't have it system-wide, run this from the Terminal tab inside Android Studio — it ships with a JDK.
|
|
156
193
|
|
|
194
|
+
`formatted="false"` skips formatting checks only; XML, resource coverage and attribute checks still apply. Java compilation uses a temporary directory, so installed package files are not modified. Verification is deliberately stricter than Android's missing-string fallback: missing translatable resources are reported. Passing these checks does not replace an Android build, native-speaker review or device layout checks.
|
|
195
|
+
|
|
157
196
|
---
|
|
158
197
|
|
|
159
198
|
### `models`
|
|
@@ -163,27 +202,33 @@ android-localise models # all providers
|
|
|
163
202
|
android-localise models --provider openai # one provider
|
|
164
203
|
```
|
|
165
204
|
|
|
166
|
-
Lists
|
|
205
|
+
Lists this CLI's configured defaults and automatic fallbacks, not the provider's entire model catalog. Any supported model can still be selected with `--model`.
|
|
167
206
|
|
|
168
207
|
---
|
|
169
208
|
|
|
170
209
|
## Providers
|
|
171
210
|
|
|
172
|
-
By default the tool uses Gemini with `gemini-3.
|
|
211
|
+
By default the tool uses Gemini with `gemini-3.8-flash`. You can switch providers with `--provider` and optionally pin a specific model with `--model`. Configured defaults and fallbacks use only the latest general-purpose text-model lineup, with defaults favoring speed and cost within that lineup.
|
|
173
212
|
|
|
174
213
|
| Provider | Default model | Fallbacks | API key env var |
|
|
175
214
|
|---|---|---|---|
|
|
176
|
-
| `gemini` _(default)_ | `gemini-3.
|
|
177
|
-
| `openai` | `gpt-
|
|
178
|
-
| `anthropic` | `claude-
|
|
215
|
+
| `gemini` _(default)_ | `gemini-3.8-flash` | none | `GEMINI_API_KEY` |
|
|
216
|
+
| `openai` | `gpt-6-luna` | `gpt-6.1-sol` → `gpt-6-astra` | `OPENAI_API_KEY` |
|
|
217
|
+
| `anthropic` | `claude-sonnet-5-5` | `claude-opus-5-5` | `ANTHROPIC_API_KEY` |
|
|
179
218
|
| `custom` | set with `--model` | none | `OPENAI_API_KEY` (optional) |
|
|
180
219
|
|
|
181
220
|
If the default model returns a "model not found" error (e.g. it was deprecated), the tool automatically retries with the next fallback. If you pin a model with `--model`, no fallback is used.
|
|
182
221
|
|
|
222
|
+
Model IDs and compatibility checked against [Google's model catalog](https://ai.google.dev/gemini-api/docs/models), [OpenAI's model catalog](https://developers.openai.com/api/docs/models) and [Anthropic's model catalog](https://platform.claude.com/docs/en/models/overview) on **2026-10-02**. Older Gemini, GPT-5 and Haiku 4.5 models are excluded from automatic selection. Gemini has no fallback in its latest stable text generation. OpenAI and Anthropic fallbacks are higher-cost models; pin `--model` to avoid automatic tier changes. Explicit model selection and custom/local endpoints remain available. The catalog is bundled with each CLI release, rather than automatically discovering models at runtime.
|
|
223
|
+
|
|
224
|
+
OpenAI and local providers retain the Chat Completions request format. Provider replies indicating truncation or blocked/incomplete output are rejected. Anthropic allows up to 16,384 output tokens per request and text blocks are collected separately from thinking blocks. Large files can still exceed a model's limits; automatic batching is future work.
|
|
225
|
+
|
|
183
226
|
**Using OpenAI:**
|
|
184
227
|
```bash
|
|
185
228
|
android-localise translate --provider openai --api-key YOUR_KEY
|
|
186
|
-
android-localise translate --provider openai --model gpt-
|
|
229
|
+
android-localise translate --provider openai --model gpt-6-luna --api-key YOUR_KEY
|
|
230
|
+
# Optional stronger tier; different cost/latency
|
|
231
|
+
android-localise translate --provider openai --model gpt-6.1-sol --api-key YOUR_KEY
|
|
187
232
|
```
|
|
188
233
|
|
|
189
234
|
**Using Anthropic:**
|
|
@@ -234,6 +279,8 @@ android-localise translate
|
|
|
234
279
|
| `OPENAI_API_KEY` | `--provider openai` and `--provider custom` |
|
|
235
280
|
| `ANTHROPIC_API_KEY` | `--provider anthropic` |
|
|
236
281
|
| `API_KEY` | fallback for any provider if the provider-specific var is not set |
|
|
282
|
+
| `ANDROID_LOCALISE_NO_UPDATE_CHECK` | Set to `1` to disable update checks/notices |
|
|
283
|
+
| `CI` | When nonempty, skips update checks/notices |
|
|
237
284
|
|
|
238
285
|
---
|
|
239
286
|
|
|
@@ -262,15 +309,15 @@ android-localise verify
|
|
|
262
309
|
./gradlew assembleDebug
|
|
263
310
|
```
|
|
264
311
|
|
|
265
|
-
After this, whenever you add or change strings in your English `strings.xml`, run the same three commands again. Existing translated strings
|
|
312
|
+
After this, whenever you add or change strings in your English `strings.xml`, run the same three commands again. Existing translated strings are refreshed by default. For additions that should retain existing translations, run `translate --missing-only` instead; it does not detect changed source text for existing keys.
|
|
266
313
|
|
|
267
314
|
---
|
|
268
315
|
|
|
269
316
|
## Platform support
|
|
270
317
|
|
|
271
|
-
|
|
318
|
+
I develop and **manually test this project on Windows only** at the moment. It is written in pure Python (stdlib only) and should run on macOS and Linux, but I have **not verified** those platforms yet.
|
|
272
319
|
|
|
273
|
-
|
|
320
|
+
I especially need help testing on:
|
|
274
321
|
|
|
275
322
|
- **macOS** — `translate`, `fix`, `verify` (including `javac` / Android Studio terminal)
|
|
276
323
|
- **Linux** — same workflow, plus common CI environments
|
|
@@ -281,7 +328,7 @@ If you use another OS, please try the [quick start](#quick-start) workflow and r
|
|
|
281
328
|
- **Broken?** — open a [bug report](https://github.com/BharathKmalviya/android-llm-localization/issues/new?template=bug_report.md) with the full error output
|
|
282
329
|
- **Want to help more?** — see [Contributing](#contributing) and [CONTRIBUTING.md](CONTRIBUTING.md)
|
|
283
330
|
|
|
284
|
-
|
|
331
|
+
PRs with cross-platform fixes and test notes are especially appreciated.
|
|
285
332
|
|
|
286
333
|
---
|
|
287
334
|
|
|
@@ -289,10 +336,13 @@ Cross-platform fixes and test notes in pull requests are very welcome.
|
|
|
289
336
|
|
|
290
337
|
| Topic | Detail |
|
|
291
338
|
|---|---|
|
|
292
|
-
| **Platform testing** |
|
|
293
|
-
| **Scope** |
|
|
294
|
-
| **
|
|
295
|
-
| **
|
|
339
|
+
| **Platform testing** | I test on **Windows only** — macOS and Linux need community verification (see [Platform support](#platform-support)) |
|
|
340
|
+
| **Scope** | Reads `values/strings.xml` only. Strings, arrays and plurals within that file are checked; separate XML files are not scanned |
|
|
341
|
+
| **Plurals** | Preserves source quantities and item structure; does not generate target-language plural categories. Review plural completeness for each language |
|
|
342
|
+
| **Overwrite** | Normal runs refresh whole files. `--missing-only` retains existing resources but does not detect source changes |
|
|
343
|
+
| **Folder scan** | Recognizes language-first and Android `b+` locale forms, with optional trailing qualifiers. MCC/MNC-prefixed resource folders are not scanned |
|
|
344
|
+
| **Validation** | Requires source attributes, inline element order and formatting options to match. DTD/entity declarations are unsupported; checks do not replace Android compilation or language review |
|
|
345
|
+
| **Large files** | One request per locale; no automatic batching or resume cache. Incomplete output is rejected |
|
|
296
346
|
| **Network** | `translate` requires internet access to reach the LLM API (except local `custom` providers) |
|
|
297
347
|
| **JDK** | `verify` requires `javac` on your PATH |
|
|
298
348
|
|
|
@@ -305,6 +355,7 @@ Cross-platform fixes and test notes in pull requests are very welcome.
|
|
|
305
355
|
| `Could not find English strings.xml` | Check `--res-dir` points to your `res/` folder and `values/strings.xml` exists |
|
|
306
356
|
| `No locale directories found` | Add `--languages hi,es,fr` or create `values-<lang>/` folders manually |
|
|
307
357
|
| API auth errors | Confirm your key env var or `--api-key` matches the `--provider` |
|
|
358
|
+
| Resource validation fails | Read the named resource error; check source/target keys, attributes, placeholders and markup. Existing files are retained |
|
|
308
359
|
| `javac` not found | Install a JDK or run `verify` from Android Studio's terminal |
|
|
309
360
|
| Build fails on apostrophes | Run `android-localise fix` before building |
|
|
310
361
|
| `%` crashes at runtime | Run `android-localise verify` — it catches bad format specifiers before release |
|
|
@@ -317,17 +368,17 @@ Cross-platform fixes and test notes in pull requests are very welcome.
|
|
|
317
368
|
- [ ] **iOS support** — translate `Localizable.strings` and `Localizable.xcstrings` for iOS/macOS apps. The LLM prompt and provider logic is already in place — it mainly needs a parser for Apple's strings format and the right folder structure (`<lang>.lproj/`). Good first contribution if you're familiar with iOS projects.
|
|
318
369
|
- [ ] **Smarter locale folder detection** — skip non-locale `values-*` qualifiers (`night`, `sw600dp`, `v21`, etc.) when scanning without `--languages`
|
|
319
370
|
- [ ] **Automated test suite** — unit tests for `fix`, XML parsing, and format-specifier edge cases
|
|
320
|
-
- [ ] **Cross-platform verification** — confirm `translate`, `fix`, and `verify` on macOS and Linux (
|
|
371
|
+
- [ ] **Cross-platform verification** — confirm `translate`, `fix`, and `verify` on macOS and Linux (I currently test on Windows only)
|
|
321
372
|
|
|
322
373
|
---
|
|
323
374
|
|
|
324
375
|
## Contributing
|
|
325
376
|
|
|
326
|
-
|
|
377
|
+
I welcome bug reports, pull requests, and **cross-platform testing**. For larger changes, please open an issue first.
|
|
327
378
|
|
|
328
379
|
**No code required** — if you are on macOS or Linux, running the tool and filing an issue (pass or fail) is a real contribution. See [Platform support](#platform-support).
|
|
329
380
|
|
|
330
|
-
See [CONTRIBUTING.md](CONTRIBUTING.md) for the
|
|
381
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for the development workflow, branch strategy, and release process.
|
|
331
382
|
|
|
332
383
|
```bash
|
|
333
384
|
git clone https://github.com/BharathKmalviya/android-llm-localization
|
|
@@ -9,10 +9,14 @@ Usage:
|
|
|
9
9
|
"""
|
|
10
10
|
|
|
11
11
|
import argparse
|
|
12
|
+
import sys
|
|
12
13
|
from android_localisation import __version__
|
|
13
14
|
|
|
14
15
|
|
|
15
|
-
def main():
|
|
16
|
+
def main(args=None):
|
|
17
|
+
for stream in (sys.stdout, sys.stderr):
|
|
18
|
+
if hasattr(stream, "reconfigure"):
|
|
19
|
+
stream.reconfigure(encoding="utf-8", errors="replace")
|
|
16
20
|
parser = argparse.ArgumentParser(
|
|
17
21
|
prog="android-localise",
|
|
18
22
|
description="Zero-dependency Android strings.xml localization using LLMs.",
|
|
@@ -37,6 +41,8 @@ def main():
|
|
|
37
41
|
help=f"Seconds to wait for each API response, up to {MAX_TIMEOUT_RETRIES + 1} attempts on timeout (default: {DEFAULT_API_TIMEOUT})",
|
|
38
42
|
)
|
|
39
43
|
translate_parser.add_argument("--languages", help="Comma-separated language codes, e.g. hi,es,fr,de — creates folders and strings.xml automatically")
|
|
44
|
+
translate_parser.add_argument("--missing-only", action="store_true", help="Translate missing resources while retaining existing translations")
|
|
45
|
+
translate_parser.add_argument("--dry-run", action="store_true", help="Generate and validate translations, then show a diff without writing files (API usage applies)")
|
|
40
46
|
|
|
41
47
|
# --- fix ---
|
|
42
48
|
fix_parser = subparsers.add_parser("fix", help="Fix XML escaping issues in translated strings.xml files")
|
|
@@ -47,23 +53,33 @@ def main():
|
|
|
47
53
|
verify_parser.add_argument("--res-dir", default="app/src/main/res", help="Path to the Android res/ directory (default: app/src/main/res)")
|
|
48
54
|
|
|
49
55
|
# --- models ---
|
|
50
|
-
models_parser = subparsers.add_parser("models", help="List
|
|
56
|
+
models_parser = subparsers.add_parser("models", help="List configured model defaults and fallbacks")
|
|
51
57
|
models_parser.add_argument("--provider", choices=["gemini", "openai", "anthropic"], default=None,
|
|
52
58
|
help="Filter by provider (shows all if not set)")
|
|
53
59
|
|
|
54
|
-
args
|
|
60
|
+
if args is None or isinstance(args, list):
|
|
61
|
+
args = parser.parse_args(args)
|
|
55
62
|
|
|
63
|
+
from android_localisation.updates import start_update_check, show_update_notice
|
|
64
|
+
update_state = start_update_check()
|
|
65
|
+
try:
|
|
66
|
+
return _run_command(args)
|
|
67
|
+
finally:
|
|
68
|
+
show_update_notice(update_state)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def _run_command(args):
|
|
56
72
|
if args.command == "translate":
|
|
57
73
|
from android_localisation.translate import main as run
|
|
58
|
-
run(args)
|
|
74
|
+
return run(args)
|
|
59
75
|
|
|
60
76
|
elif args.command == "fix":
|
|
61
77
|
from android_localisation.fix import main as run
|
|
62
|
-
run(args)
|
|
78
|
+
return run(args)
|
|
63
79
|
|
|
64
80
|
elif args.command == "verify":
|
|
65
81
|
from android_localisation.verify import main as run
|
|
66
|
-
run(args)
|
|
82
|
+
return run(args)
|
|
67
83
|
|
|
68
84
|
elif args.command == "models":
|
|
69
85
|
from android_localisation.translate import PROVIDER_MODELS
|
|
@@ -80,9 +96,9 @@ def main():
|
|
|
80
96
|
print(" → Any model name your local server supports (must use --model)")
|
|
81
97
|
print()
|
|
82
98
|
print(" Tip: use --model to pick any model, e.g:")
|
|
83
|
-
print(" android-localise translate --provider openai --model gpt-
|
|
99
|
+
print(" android-localise translate --provider openai --model gpt-6-luna --api-key KEY")
|
|
84
100
|
print()
|
|
85
101
|
|
|
86
102
|
|
|
87
103
|
if __name__ == "__main__":
|
|
88
|
-
main()
|
|
104
|
+
raise SystemExit(main())
|