android-localisation 1.0.7__tar.gz → 1.1.1__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.1}/PKG-INFO +59 -28
- {android_localisation-1.0.7 → android_localisation-1.1.1}/README.md +58 -27
- {android_localisation-1.0.7 → android_localisation-1.1.1}/android_localisation/__init__.py +1 -1
- {android_localisation-1.0.7 → android_localisation-1.1.1}/android_localisation/cli.py +12 -6
- {android_localisation-1.0.7 → android_localisation-1.1.1}/android_localisation/fix.py +39 -17
- android_localisation-1.1.1/android_localisation/java/VerifyStrings.java +138 -0
- android_localisation-1.1.1/android_localisation/resources.py +214 -0
- {android_localisation-1.0.7 → android_localisation-1.1.1}/android_localisation/translate.py +117 -72
- android_localisation-1.1.1/android_localisation/verify.py +82 -0
- {android_localisation-1.0.7 → android_localisation-1.1.1/android_localisation.egg-info}/PKG-INFO +59 -28
- {android_localisation-1.0.7 → android_localisation-1.1.1}/android_localisation.egg-info/SOURCES.txt +1 -0
- {android_localisation-1.0.7 → android_localisation-1.1.1}/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.1}/LICENSE +0 -0
- {android_localisation-1.0.7 → android_localisation-1.1.1}/MANIFEST.in +0 -0
- {android_localisation-1.0.7 → android_localisation-1.1.1}/android_localisation.egg-info/dependency_links.txt +0 -0
- {android_localisation-1.0.7 → android_localisation-1.1.1}/android_localisation.egg-info/entry_points.txt +0 -0
- {android_localisation-1.0.7 → android_localisation-1.1.1}/android_localisation.egg-info/top_level.txt +0 -0
- {android_localisation-1.0.7 → android_localisation-1.1.1}/setup.cfg +0 -0
{android_localisation-1.0.7/android_localisation.egg-info → android_localisation-1.1.1}/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.1
|
|
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,8 @@ 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
|
+
|
|
57
59
|
---
|
|
58
60
|
|
|
59
61
|
## Quick start
|
|
@@ -65,7 +67,7 @@ android-localise translate --api-key YOUR_GEMINI_KEY
|
|
|
65
67
|
# Step 2 — fix any formatting issues the LLM may have introduced
|
|
66
68
|
android-localise fix
|
|
67
69
|
|
|
68
|
-
# Step 3 —
|
|
70
|
+
# Step 3 — check resources and Java formatting
|
|
69
71
|
android-localise verify
|
|
70
72
|
```
|
|
71
73
|
|
|
@@ -78,9 +80,9 @@ That's the full workflow. Run these three commands after every time you update y
|
|
|
78
80
|
When you run `android-localise translate --api-key YOUR_KEY`, here's exactly what it does:
|
|
79
81
|
|
|
80
82
|
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.
|
|
83
|
+
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`
|
|
84
|
+
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
|
|
85
|
+
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
86
|
5. Waits 5 seconds between each language request to avoid hitting API rate limits
|
|
85
87
|
|
|
86
88
|
**Defaults used when you don't specify anything:**
|
|
@@ -88,12 +90,14 @@ When you run `android-localise translate --api-key YOUR_KEY`, here's exactly wha
|
|
|
88
90
|
| What | Default |
|
|
89
91
|
|---|---|
|
|
90
92
|
| Provider | Gemini |
|
|
91
|
-
| Model | `gemini-3.
|
|
93
|
+
| Model | `gemini-3.8-flash` |
|
|
92
94
|
| Source directory | `app/src/main/res` |
|
|
93
95
|
| Delay between requests | 5 seconds |
|
|
94
96
|
| App context | none (generic prompt) |
|
|
95
97
|
|
|
96
|
-
|
|
98
|
+
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**.
|
|
99
|
+
|
|
100
|
+
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
101
|
|
|
98
102
|
---
|
|
99
103
|
|
|
@@ -107,7 +111,7 @@ For target languages, you have two options:
|
|
|
107
111
|
```bash
|
|
108
112
|
android-localise translate --api-key YOUR_KEY --languages hi,es,fr,de
|
|
109
113
|
```
|
|
110
|
-
This
|
|
114
|
+
This translates into Hindi, Spanish, French and German, creating each folder and `strings.xml` when its translation passes validation.
|
|
111
115
|
|
|
112
116
|
**Option B — pre-create folders yourself:**
|
|
113
117
|
```
|
|
@@ -118,7 +122,7 @@ app/src/main/res/
|
|
|
118
122
|
├── values-es/
|
|
119
123
|
└── values-fr/
|
|
120
124
|
```
|
|
121
|
-
Run `android-localise translate --api-key YOUR_KEY` and it picks up
|
|
125
|
+
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
126
|
|
|
123
127
|
**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
128
|
|
|
@@ -153,6 +157,19 @@ android-localise translate \
|
|
|
153
157
|
| `--base-url` | API endpoint for local/custom providers | — |
|
|
154
158
|
| `--sleep` | Seconds to wait between language requests | `5.0` |
|
|
155
159
|
| `--timeout` | Seconds to wait for each API response (up to 3 attempts on timeout) | `180` |
|
|
160
|
+
| `--missing-only` | Translate absent resources; retain existing translations | off (refresh whole file) |
|
|
161
|
+
| `--dry-run` | Generate and validate output, then print a diff without writing files | off |
|
|
162
|
+
|
|
163
|
+
**Preserve reviewed translations:**
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
android-localise translate --languages hi,es --missing-only
|
|
167
|
+
android-localise translate --languages hi --missing-only --dry-run
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
`--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.
|
|
171
|
+
|
|
172
|
+
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
173
|
|
|
157
174
|
---
|
|
158
175
|
|
|
@@ -163,12 +180,14 @@ android-localise fix
|
|
|
163
180
|
android-localise fix --res-dir path/to/res
|
|
164
181
|
```
|
|
165
182
|
|
|
166
|
-
|
|
183
|
+
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
184
|
|
|
168
185
|
Strings marked `formatted="false"` are skipped — their `%` signs are literal, not format specifiers.
|
|
169
186
|
|
|
170
187
|
Always run this before `verify` and before building.
|
|
171
188
|
|
|
189
|
+
`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.
|
|
190
|
+
|
|
172
191
|
---
|
|
173
192
|
|
|
174
193
|
### `verify`
|
|
@@ -178,10 +197,12 @@ android-localise verify
|
|
|
178
197
|
android-localise verify --res-dir path/to/res
|
|
179
198
|
```
|
|
180
199
|
|
|
181
|
-
|
|
200
|
+
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
201
|
|
|
183
202
|
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
203
|
|
|
204
|
+
`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.
|
|
205
|
+
|
|
185
206
|
---
|
|
186
207
|
|
|
187
208
|
### `models`
|
|
@@ -191,27 +212,33 @@ android-localise models # all providers
|
|
|
191
212
|
android-localise models --provider openai # one provider
|
|
192
213
|
```
|
|
193
214
|
|
|
194
|
-
Lists
|
|
215
|
+
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
216
|
|
|
196
217
|
---
|
|
197
218
|
|
|
198
219
|
## Providers
|
|
199
220
|
|
|
200
|
-
By default the tool uses Gemini with `gemini-3.
|
|
221
|
+
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
222
|
|
|
202
223
|
| Provider | Default model | Fallbacks | API key env var |
|
|
203
224
|
|---|---|---|---|
|
|
204
|
-
| `gemini` _(default)_ | `gemini-3.
|
|
205
|
-
| `openai` | `gpt-
|
|
206
|
-
| `anthropic` | `claude-
|
|
225
|
+
| `gemini` _(default)_ | `gemini-3.8-flash` | none | `GEMINI_API_KEY` |
|
|
226
|
+
| `openai` | `gpt-6-luna` | `gpt-6.1-sol` → `gpt-6-astra` | `OPENAI_API_KEY` |
|
|
227
|
+
| `anthropic` | `claude-sonnet-5-5` | `claude-opus-5-5` | `ANTHROPIC_API_KEY` |
|
|
207
228
|
| `custom` | set with `--model` | none | `OPENAI_API_KEY` (optional) |
|
|
208
229
|
|
|
209
230
|
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
231
|
|
|
232
|
+
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.
|
|
233
|
+
|
|
234
|
+
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.
|
|
235
|
+
|
|
211
236
|
**Using OpenAI:**
|
|
212
237
|
```bash
|
|
213
238
|
android-localise translate --provider openai --api-key YOUR_KEY
|
|
214
|
-
android-localise translate --provider openai --model gpt-
|
|
239
|
+
android-localise translate --provider openai --model gpt-6-luna --api-key YOUR_KEY
|
|
240
|
+
# Optional stronger tier; different cost/latency
|
|
241
|
+
android-localise translate --provider openai --model gpt-6.1-sol --api-key YOUR_KEY
|
|
215
242
|
```
|
|
216
243
|
|
|
217
244
|
**Using Anthropic:**
|
|
@@ -290,15 +317,15 @@ android-localise verify
|
|
|
290
317
|
./gradlew assembleDebug
|
|
291
318
|
```
|
|
292
319
|
|
|
293
|
-
After this, whenever you add or change strings in your English `strings.xml`, run the same three commands again. Existing translated strings
|
|
320
|
+
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
321
|
|
|
295
322
|
---
|
|
296
323
|
|
|
297
324
|
## Platform support
|
|
298
325
|
|
|
299
|
-
|
|
326
|
+
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
327
|
|
|
301
|
-
|
|
328
|
+
I especially need help testing on:
|
|
302
329
|
|
|
303
330
|
- **macOS** — `translate`, `fix`, `verify` (including `javac` / Android Studio terminal)
|
|
304
331
|
- **Linux** — same workflow, plus common CI environments
|
|
@@ -309,7 +336,7 @@ If you use another OS, please try the [quick start](#quick-start) workflow and r
|
|
|
309
336
|
- **Broken?** — open a [bug report](https://github.com/BharathKmalviya/android-llm-localization/issues/new?template=bug_report.md) with the full error output
|
|
310
337
|
- **Want to help more?** — see [Contributing](#contributing) and [CONTRIBUTING.md](CONTRIBUTING.md)
|
|
311
338
|
|
|
312
|
-
|
|
339
|
+
PRs with cross-platform fixes and test notes are especially appreciated.
|
|
313
340
|
|
|
314
341
|
---
|
|
315
342
|
|
|
@@ -317,10 +344,13 @@ Cross-platform fixes and test notes in pull requests are very welcome.
|
|
|
317
344
|
|
|
318
345
|
| Topic | Detail |
|
|
319
346
|
|---|---|
|
|
320
|
-
| **Platform testing** |
|
|
321
|
-
| **Scope** |
|
|
322
|
-
| **
|
|
323
|
-
| **
|
|
347
|
+
| **Platform testing** | I test on **Windows only** — macOS and Linux need community verification (see [Platform support](#platform-support)) |
|
|
348
|
+
| **Scope** | Reads `values/strings.xml` only. Strings, arrays and plurals within that file are checked; separate XML files are not scanned |
|
|
349
|
+
| **Plurals** | Preserves source quantities and item structure; does not generate target-language plural categories. Review plural completeness for each language |
|
|
350
|
+
| **Overwrite** | Normal runs refresh whole files. `--missing-only` retains existing resources but does not detect source changes |
|
|
351
|
+
| **Folder scan** | Recognizes language-first and Android `b+` locale forms, with optional trailing qualifiers. MCC/MNC-prefixed resource folders are not scanned |
|
|
352
|
+
| **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 |
|
|
353
|
+
| **Large files** | One request per locale; no automatic batching or resume cache. Incomplete output is rejected |
|
|
324
354
|
| **Network** | `translate` requires internet access to reach the LLM API (except local `custom` providers) |
|
|
325
355
|
| **JDK** | `verify` requires `javac` on your PATH |
|
|
326
356
|
|
|
@@ -333,6 +363,7 @@ Cross-platform fixes and test notes in pull requests are very welcome.
|
|
|
333
363
|
| `Could not find English strings.xml` | Check `--res-dir` points to your `res/` folder and `values/strings.xml` exists |
|
|
334
364
|
| `No locale directories found` | Add `--languages hi,es,fr` or create `values-<lang>/` folders manually |
|
|
335
365
|
| API auth errors | Confirm your key env var or `--api-key` matches the `--provider` |
|
|
366
|
+
| Resource validation fails | Read the named resource error; check source/target keys, attributes, placeholders and markup. Existing files are retained |
|
|
336
367
|
| `javac` not found | Install a JDK or run `verify` from Android Studio's terminal |
|
|
337
368
|
| Build fails on apostrophes | Run `android-localise fix` before building |
|
|
338
369
|
| `%` crashes at runtime | Run `android-localise verify` — it catches bad format specifiers before release |
|
|
@@ -345,17 +376,17 @@ Cross-platform fixes and test notes in pull requests are very welcome.
|
|
|
345
376
|
- [ ] **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
377
|
- [ ] **Smarter locale folder detection** — skip non-locale `values-*` qualifiers (`night`, `sw600dp`, `v21`, etc.) when scanning without `--languages`
|
|
347
378
|
- [ ] **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 (
|
|
379
|
+
- [ ] **Cross-platform verification** — confirm `translate`, `fix`, and `verify` on macOS and Linux (I currently test on Windows only)
|
|
349
380
|
|
|
350
381
|
---
|
|
351
382
|
|
|
352
383
|
## Contributing
|
|
353
384
|
|
|
354
|
-
|
|
385
|
+
I welcome bug reports, pull requests, and **cross-platform testing**. For larger changes, please open an issue first.
|
|
355
386
|
|
|
356
387
|
**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
388
|
|
|
358
|
-
See [CONTRIBUTING.md](CONTRIBUTING.md) for the
|
|
389
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for the development workflow, branch strategy, and release process.
|
|
359
390
|
|
|
360
391
|
```bash
|
|
361
392
|
git clone https://github.com/BharathKmalviya/android-llm-localization
|
|
@@ -26,6 +26,8 @@ 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
|
+
|
|
29
31
|
---
|
|
30
32
|
|
|
31
33
|
## Quick start
|
|
@@ -37,7 +39,7 @@ android-localise translate --api-key YOUR_GEMINI_KEY
|
|
|
37
39
|
# Step 2 — fix any formatting issues the LLM may have introduced
|
|
38
40
|
android-localise fix
|
|
39
41
|
|
|
40
|
-
# Step 3 —
|
|
42
|
+
# Step 3 — check resources and Java formatting
|
|
41
43
|
android-localise verify
|
|
42
44
|
```
|
|
43
45
|
|
|
@@ -50,9 +52,9 @@ That's the full workflow. Run these three commands after every time you update y
|
|
|
50
52
|
When you run `android-localise translate --api-key YOUR_KEY`, here's exactly what it does:
|
|
51
53
|
|
|
52
54
|
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.
|
|
55
|
+
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`
|
|
56
|
+
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
|
|
57
|
+
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
58
|
5. Waits 5 seconds between each language request to avoid hitting API rate limits
|
|
57
59
|
|
|
58
60
|
**Defaults used when you don't specify anything:**
|
|
@@ -60,12 +62,14 @@ When you run `android-localise translate --api-key YOUR_KEY`, here's exactly wha
|
|
|
60
62
|
| What | Default |
|
|
61
63
|
|---|---|
|
|
62
64
|
| Provider | Gemini |
|
|
63
|
-
| Model | `gemini-3.
|
|
65
|
+
| Model | `gemini-3.8-flash` |
|
|
64
66
|
| Source directory | `app/src/main/res` |
|
|
65
67
|
| Delay between requests | 5 seconds |
|
|
66
68
|
| App context | none (generic prompt) |
|
|
67
69
|
|
|
68
|
-
|
|
70
|
+
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**.
|
|
71
|
+
|
|
72
|
+
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
73
|
|
|
70
74
|
---
|
|
71
75
|
|
|
@@ -79,7 +83,7 @@ For target languages, you have two options:
|
|
|
79
83
|
```bash
|
|
80
84
|
android-localise translate --api-key YOUR_KEY --languages hi,es,fr,de
|
|
81
85
|
```
|
|
82
|
-
This
|
|
86
|
+
This translates into Hindi, Spanish, French and German, creating each folder and `strings.xml` when its translation passes validation.
|
|
83
87
|
|
|
84
88
|
**Option B — pre-create folders yourself:**
|
|
85
89
|
```
|
|
@@ -90,7 +94,7 @@ app/src/main/res/
|
|
|
90
94
|
├── values-es/
|
|
91
95
|
└── values-fr/
|
|
92
96
|
```
|
|
93
|
-
Run `android-localise translate --api-key YOUR_KEY` and it picks up
|
|
97
|
+
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
98
|
|
|
95
99
|
**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
100
|
|
|
@@ -125,6 +129,19 @@ android-localise translate \
|
|
|
125
129
|
| `--base-url` | API endpoint for local/custom providers | — |
|
|
126
130
|
| `--sleep` | Seconds to wait between language requests | `5.0` |
|
|
127
131
|
| `--timeout` | Seconds to wait for each API response (up to 3 attempts on timeout) | `180` |
|
|
132
|
+
| `--missing-only` | Translate absent resources; retain existing translations | off (refresh whole file) |
|
|
133
|
+
| `--dry-run` | Generate and validate output, then print a diff without writing files | off |
|
|
134
|
+
|
|
135
|
+
**Preserve reviewed translations:**
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
android-localise translate --languages hi,es --missing-only
|
|
139
|
+
android-localise translate --languages hi --missing-only --dry-run
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
`--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.
|
|
143
|
+
|
|
144
|
+
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
145
|
|
|
129
146
|
---
|
|
130
147
|
|
|
@@ -135,12 +152,14 @@ android-localise fix
|
|
|
135
152
|
android-localise fix --res-dir path/to/res
|
|
136
153
|
```
|
|
137
154
|
|
|
138
|
-
|
|
155
|
+
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
156
|
|
|
140
157
|
Strings marked `formatted="false"` are skipped — their `%` signs are literal, not format specifiers.
|
|
141
158
|
|
|
142
159
|
Always run this before `verify` and before building.
|
|
143
160
|
|
|
161
|
+
`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.
|
|
162
|
+
|
|
144
163
|
---
|
|
145
164
|
|
|
146
165
|
### `verify`
|
|
@@ -150,10 +169,12 @@ android-localise verify
|
|
|
150
169
|
android-localise verify --res-dir path/to/res
|
|
151
170
|
```
|
|
152
171
|
|
|
153
|
-
|
|
172
|
+
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
173
|
|
|
155
174
|
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
175
|
|
|
176
|
+
`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.
|
|
177
|
+
|
|
157
178
|
---
|
|
158
179
|
|
|
159
180
|
### `models`
|
|
@@ -163,27 +184,33 @@ android-localise models # all providers
|
|
|
163
184
|
android-localise models --provider openai # one provider
|
|
164
185
|
```
|
|
165
186
|
|
|
166
|
-
Lists
|
|
187
|
+
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
188
|
|
|
168
189
|
---
|
|
169
190
|
|
|
170
191
|
## Providers
|
|
171
192
|
|
|
172
|
-
By default the tool uses Gemini with `gemini-3.
|
|
193
|
+
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
194
|
|
|
174
195
|
| Provider | Default model | Fallbacks | API key env var |
|
|
175
196
|
|---|---|---|---|
|
|
176
|
-
| `gemini` _(default)_ | `gemini-3.
|
|
177
|
-
| `openai` | `gpt-
|
|
178
|
-
| `anthropic` | `claude-
|
|
197
|
+
| `gemini` _(default)_ | `gemini-3.8-flash` | none | `GEMINI_API_KEY` |
|
|
198
|
+
| `openai` | `gpt-6-luna` | `gpt-6.1-sol` → `gpt-6-astra` | `OPENAI_API_KEY` |
|
|
199
|
+
| `anthropic` | `claude-sonnet-5-5` | `claude-opus-5-5` | `ANTHROPIC_API_KEY` |
|
|
179
200
|
| `custom` | set with `--model` | none | `OPENAI_API_KEY` (optional) |
|
|
180
201
|
|
|
181
202
|
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
203
|
|
|
204
|
+
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.
|
|
205
|
+
|
|
206
|
+
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.
|
|
207
|
+
|
|
183
208
|
**Using OpenAI:**
|
|
184
209
|
```bash
|
|
185
210
|
android-localise translate --provider openai --api-key YOUR_KEY
|
|
186
|
-
android-localise translate --provider openai --model gpt-
|
|
211
|
+
android-localise translate --provider openai --model gpt-6-luna --api-key YOUR_KEY
|
|
212
|
+
# Optional stronger tier; different cost/latency
|
|
213
|
+
android-localise translate --provider openai --model gpt-6.1-sol --api-key YOUR_KEY
|
|
187
214
|
```
|
|
188
215
|
|
|
189
216
|
**Using Anthropic:**
|
|
@@ -262,15 +289,15 @@ android-localise verify
|
|
|
262
289
|
./gradlew assembleDebug
|
|
263
290
|
```
|
|
264
291
|
|
|
265
|
-
After this, whenever you add or change strings in your English `strings.xml`, run the same three commands again. Existing translated strings
|
|
292
|
+
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
293
|
|
|
267
294
|
---
|
|
268
295
|
|
|
269
296
|
## Platform support
|
|
270
297
|
|
|
271
|
-
|
|
298
|
+
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
299
|
|
|
273
|
-
|
|
300
|
+
I especially need help testing on:
|
|
274
301
|
|
|
275
302
|
- **macOS** — `translate`, `fix`, `verify` (including `javac` / Android Studio terminal)
|
|
276
303
|
- **Linux** — same workflow, plus common CI environments
|
|
@@ -281,7 +308,7 @@ If you use another OS, please try the [quick start](#quick-start) workflow and r
|
|
|
281
308
|
- **Broken?** — open a [bug report](https://github.com/BharathKmalviya/android-llm-localization/issues/new?template=bug_report.md) with the full error output
|
|
282
309
|
- **Want to help more?** — see [Contributing](#contributing) and [CONTRIBUTING.md](CONTRIBUTING.md)
|
|
283
310
|
|
|
284
|
-
|
|
311
|
+
PRs with cross-platform fixes and test notes are especially appreciated.
|
|
285
312
|
|
|
286
313
|
---
|
|
287
314
|
|
|
@@ -289,10 +316,13 @@ Cross-platform fixes and test notes in pull requests are very welcome.
|
|
|
289
316
|
|
|
290
317
|
| Topic | Detail |
|
|
291
318
|
|---|---|
|
|
292
|
-
| **Platform testing** |
|
|
293
|
-
| **Scope** |
|
|
294
|
-
| **
|
|
295
|
-
| **
|
|
319
|
+
| **Platform testing** | I test on **Windows only** — macOS and Linux need community verification (see [Platform support](#platform-support)) |
|
|
320
|
+
| **Scope** | Reads `values/strings.xml` only. Strings, arrays and plurals within that file are checked; separate XML files are not scanned |
|
|
321
|
+
| **Plurals** | Preserves source quantities and item structure; does not generate target-language plural categories. Review plural completeness for each language |
|
|
322
|
+
| **Overwrite** | Normal runs refresh whole files. `--missing-only` retains existing resources but does not detect source changes |
|
|
323
|
+
| **Folder scan** | Recognizes language-first and Android `b+` locale forms, with optional trailing qualifiers. MCC/MNC-prefixed resource folders are not scanned |
|
|
324
|
+
| **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 |
|
|
325
|
+
| **Large files** | One request per locale; no automatic batching or resume cache. Incomplete output is rejected |
|
|
296
326
|
| **Network** | `translate` requires internet access to reach the LLM API (except local `custom` providers) |
|
|
297
327
|
| **JDK** | `verify` requires `javac` on your PATH |
|
|
298
328
|
|
|
@@ -305,6 +335,7 @@ Cross-platform fixes and test notes in pull requests are very welcome.
|
|
|
305
335
|
| `Could not find English strings.xml` | Check `--res-dir` points to your `res/` folder and `values/strings.xml` exists |
|
|
306
336
|
| `No locale directories found` | Add `--languages hi,es,fr` or create `values-<lang>/` folders manually |
|
|
307
337
|
| API auth errors | Confirm your key env var or `--api-key` matches the `--provider` |
|
|
338
|
+
| Resource validation fails | Read the named resource error; check source/target keys, attributes, placeholders and markup. Existing files are retained |
|
|
308
339
|
| `javac` not found | Install a JDK or run `verify` from Android Studio's terminal |
|
|
309
340
|
| Build fails on apostrophes | Run `android-localise fix` before building |
|
|
310
341
|
| `%` crashes at runtime | Run `android-localise verify` — it catches bad format specifiers before release |
|
|
@@ -317,17 +348,17 @@ Cross-platform fixes and test notes in pull requests are very welcome.
|
|
|
317
348
|
- [ ] **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
349
|
- [ ] **Smarter locale folder detection** — skip non-locale `values-*` qualifiers (`night`, `sw600dp`, `v21`, etc.) when scanning without `--languages`
|
|
319
350
|
- [ ] **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 (
|
|
351
|
+
- [ ] **Cross-platform verification** — confirm `translate`, `fix`, and `verify` on macOS and Linux (I currently test on Windows only)
|
|
321
352
|
|
|
322
353
|
---
|
|
323
354
|
|
|
324
355
|
## Contributing
|
|
325
356
|
|
|
326
|
-
|
|
357
|
+
I welcome bug reports, pull requests, and **cross-platform testing**. For larger changes, please open an issue first.
|
|
327
358
|
|
|
328
359
|
**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
360
|
|
|
330
|
-
See [CONTRIBUTING.md](CONTRIBUTING.md) for the
|
|
361
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for the development workflow, branch strategy, and release process.
|
|
331
362
|
|
|
332
363
|
```bash
|
|
333
364
|
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
16
|
def main():
|
|
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,7 +53,7 @@ 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
|
|
|
@@ -55,15 +61,15 @@ def main():
|
|
|
55
61
|
|
|
56
62
|
if args.command == "translate":
|
|
57
63
|
from android_localisation.translate import main as run
|
|
58
|
-
run(args)
|
|
64
|
+
return run(args)
|
|
59
65
|
|
|
60
66
|
elif args.command == "fix":
|
|
61
67
|
from android_localisation.fix import main as run
|
|
62
|
-
run(args)
|
|
68
|
+
return run(args)
|
|
63
69
|
|
|
64
70
|
elif args.command == "verify":
|
|
65
71
|
from android_localisation.verify import main as run
|
|
66
|
-
run(args)
|
|
72
|
+
return run(args)
|
|
67
73
|
|
|
68
74
|
elif args.command == "models":
|
|
69
75
|
from android_localisation.translate import PROVIDER_MODELS
|
|
@@ -80,9 +86,9 @@ def main():
|
|
|
80
86
|
print(" → Any model name your local server supports (must use --model)")
|
|
81
87
|
print()
|
|
82
88
|
print(" Tip: use --model to pick any model, e.g:")
|
|
83
|
-
print(" android-localise translate --provider openai --model gpt-
|
|
89
|
+
print(" android-localise translate --provider openai --model gpt-6-luna --api-key KEY")
|
|
84
90
|
print()
|
|
85
91
|
|
|
86
92
|
|
|
87
93
|
if __name__ == "__main__":
|
|
88
|
-
main()
|
|
94
|
+
raise SystemExit(main())
|