android-localisation 1.0.1__tar.gz → 1.0.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.
Files changed (20) hide show
  1. android_localisation-1.0.2/PKG-INFO +303 -0
  2. android_localisation-1.0.2/README.md +275 -0
  3. {android_localisation-1.0.1 → android_localisation-1.0.2}/android_localisation/__init__.py +1 -1
  4. {android_localisation-1.0.1 → android_localisation-1.0.2}/android_localisation/cli.py +1 -0
  5. {android_localisation-1.0.1 → android_localisation-1.0.2}/android_localisation/translate.py +40 -4
  6. android_localisation-1.0.2/android_localisation.egg-info/PKG-INFO +303 -0
  7. {android_localisation-1.0.1 → android_localisation-1.0.2}/pyproject.toml +2 -2
  8. android_localisation-1.0.1/PKG-INFO +0 -171
  9. android_localisation-1.0.1/README.md +0 -143
  10. android_localisation-1.0.1/android_localisation.egg-info/PKG-INFO +0 -171
  11. {android_localisation-1.0.1 → android_localisation-1.0.2}/LICENSE +0 -0
  12. {android_localisation-1.0.1 → android_localisation-1.0.2}/MANIFEST.in +0 -0
  13. {android_localisation-1.0.1 → android_localisation-1.0.2}/android_localisation/fix.py +0 -0
  14. {android_localisation-1.0.1 → android_localisation-1.0.2}/android_localisation/java/VerifyStrings.java +0 -0
  15. {android_localisation-1.0.1 → android_localisation-1.0.2}/android_localisation/verify.py +0 -0
  16. {android_localisation-1.0.1 → android_localisation-1.0.2}/android_localisation.egg-info/SOURCES.txt +0 -0
  17. {android_localisation-1.0.1 → android_localisation-1.0.2}/android_localisation.egg-info/dependency_links.txt +0 -0
  18. {android_localisation-1.0.1 → android_localisation-1.0.2}/android_localisation.egg-info/entry_points.txt +0 -0
  19. {android_localisation-1.0.1 → android_localisation-1.0.2}/android_localisation.egg-info/top_level.txt +0 -0
  20. {android_localisation-1.0.1 → android_localisation-1.0.2}/setup.cfg +0 -0
@@ -0,0 +1,303 @@
1
+ Metadata-Version: 2.4
2
+ Name: android-localisation
3
+ Version: 1.0.2
4
+ Summary: Zero-dependency Android strings.xml translation and verification using LLMs (Gemini, OpenAI, Anthropic, Ollama).
5
+ License: MIT
6
+ Project-URL: Homepage, https://github.com/BharathKmalviya/android-llm-localization
7
+ Project-URL: Repository, https://github.com/BharathKmalviya/android-llm-localization
8
+ Project-URL: Issues, https://github.com/BharathKmalviya/android-llm-localization/issues
9
+ Project-URL: Changelog, https://github.com/BharathKmalviya/android-llm-localization/blob/master/CHANGELOG.md
10
+ Keywords: android,localisation,localization,strings,strings-xml,translation,llm,gemini,openai,anthropic,ollama,i18n,l10n,android-development,mobile,cli
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Environment :: Console
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.8
18
+ Classifier: Programming Language :: Python :: 3.9
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Topic :: Software Development :: Internationalization
23
+ Classifier: Topic :: Software Development :: Localization
24
+ Requires-Python: >=3.8
25
+ Description-Content-Type: text/markdown
26
+ License-File: LICENSE
27
+ Dynamic: license-file
28
+
29
+ # android-llm-localization
30
+
31
+ [![PyPI version](https://img.shields.io/pypi/v/android-localisation.svg)](https://pypi.org/project/android-localisation/)
32
+ [![Python 3.8+](https://img.shields.io/badge/python-3.8+-blue.svg)](https://pypi.org/project/android-localisation/)
33
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
34
+
35
+ Translate your Android `strings.xml` into multiple languages using AI — Gemini, OpenAI, Anthropic, or a local model via Ollama. No paid service, no CSV exports, no copy-paste.
36
+
37
+ ---
38
+
39
+ ## The problem
40
+
41
+ Localizing an Android app the usual way means exporting strings, running them through Google Translate or some dashboard, cleaning up the output, and re-importing — for every language, every update. It's slow, error-prone, and the translations often feel robotic.
42
+
43
+ This tool does it differently. It reads your `strings.xml`, sends it to an LLM with context about your app, and writes the translated files directly into your project. The model understands UI language, keeps format specifiers intact, and produces natural-sounding output rather than word-for-word translations.
44
+
45
+ ---
46
+
47
+ ## Installation
48
+
49
+ ```bash
50
+ pip install android-localisation
51
+ ```
52
+
53
+ Requires Python 3.8+. No other dependencies.
54
+
55
+ ---
56
+
57
+ ## Quick start
58
+
59
+ ```bash
60
+ # Step 1 — translate
61
+ android-localise translate --api-key YOUR_GEMINI_KEY
62
+
63
+ # Step 2 — fix any formatting issues the LLM may have introduced
64
+ android-localise fix
65
+
66
+ # Step 3 — verify nothing will crash at runtime
67
+ android-localise verify
68
+ ```
69
+
70
+ That's the full workflow. Run these three commands after every time you update your English strings.
71
+
72
+ ---
73
+
74
+ ## What happens when you run translate
75
+
76
+ When you run `android-localise translate --api-key YOUR_KEY`, here's exactly what it does:
77
+
78
+ 1. Looks for `app/src/main/res/values/strings.xml` — this is your English source
79
+ 2. If `--languages` is provided, creates any missing `values-<lang>/` folders automatically. Otherwise scans the `res/` directory for existing `values-*` folders
80
+ 3. For each locale, if `strings.xml` doesn't exist it creates the file first, then sends your full English XML to the LLM with a prompt that instructs it to translate naturally, preserve all XML structure, and never touch format specifiers like `%1$s` or `%d`
81
+ 4. Writes the translated `strings.xml` directly into each locale folder
82
+ 5. Waits 5 seconds between each language request to avoid hitting API rate limits
83
+
84
+ **Defaults used when you don't specify anything:**
85
+
86
+ | What | Default |
87
+ |---|---|
88
+ | Provider | Gemini |
89
+ | Model | `gemini-2.5-flash` |
90
+ | Source directory | `app/src/main/res` |
91
+ | Delay between requests | 5 seconds |
92
+ | App context | none (generic prompt) |
93
+
94
+ Nothing is modified unless the translation comes back with valid XML. If a request fails, that language is skipped and logged — other languages continue.
95
+
96
+ ---
97
+
98
+ ## Setup
99
+
100
+ The only requirement is that `app/src/main/res/values/strings.xml` exists — your English source file.
101
+
102
+ For target languages, you have two options:
103
+
104
+ **Option A — let the tool create everything:**
105
+ ```bash
106
+ android-localise translate --api-key YOUR_KEY --languages hi,es,fr,de
107
+ ```
108
+ This creates `values-hi/`, `values-es/`, `values-fr/`, `values-de/` folders and their `strings.xml` files automatically, then translates into each one.
109
+
110
+ **Option B — pre-create folders yourself:**
111
+ ```
112
+ app/src/main/res/
113
+ ├── values/ ← your English source (must exist)
114
+ │ └── strings.xml
115
+ ├── values-hi/ ← empty folder is fine
116
+ ├── values-es/
117
+ └── values-fr/
118
+ ```
119
+ Run `android-localise translate --api-key YOUR_KEY` and it picks up any `values-*` folder it finds, creating `strings.xml` inside each one if it doesn't exist yet.
120
+
121
+ **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.
122
+
123
+ ---
124
+
125
+ ## Commands
126
+
127
+ ### `translate`
128
+
129
+ ```bash
130
+ android-localise translate --api-key YOUR_KEY
131
+ ```
132
+
133
+ Add `--app-context` with a one-line description of your app. This meaningfully improves translation quality — the model knows whether "record" means a music track, a health log, or a database entry:
134
+
135
+ ```bash
136
+ android-localise translate \
137
+ --api-key YOUR_KEY \
138
+ --app-context "a workout tracking app for gym beginners"
139
+ ```
140
+
141
+ **All flags:**
142
+
143
+ | Flag | What it does | Default |
144
+ |---|---|---|
145
+ | `--api-key` | Your API key | reads from env var |
146
+ | `--provider` | Which AI to use: `gemini` `openai` `anthropic` `custom` | `gemini` |
147
+ | `--model` | Specific model to use | see [Providers](#providers) |
148
+ | `--languages` | Comma-separated language codes — creates folders and files automatically | — |
149
+ | `--app-context` | One-line description of your app | — |
150
+ | `--res-dir` | Path to your `res/` folder | `app/src/main/res` |
151
+ | `--base-url` | API endpoint for local/custom providers | — |
152
+ | `--sleep` | Seconds to wait between language requests | `5.0` |
153
+
154
+ ---
155
+
156
+ ### `fix`
157
+
158
+ ```bash
159
+ android-localise fix
160
+ ```
161
+
162
+ LLMs occasionally produce output that looks correct but breaks the Android build — curly apostrophes (`'`) instead of escaped ones (`\'`), unescaped double quotes, or mangled `%` signs. This command scans every translated `strings.xml` and corrects these silently.
163
+
164
+ Always run this before `verify` and before building.
165
+
166
+ ---
167
+
168
+ ### `verify`
169
+
170
+ ```bash
171
+ android-localise verify
172
+ ```
173
+
174
+ Takes every translated string that contains a format specifier (`%1$s`, `%d`, `%1$f`, etc.) and calls `String.format()` on it using Java's actual runtime. If a translated string would throw `UnknownFormatConversionException` or `MissingFormatArgumentException` in your app, this catches it before your users do.
175
+
176
+ 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.
177
+
178
+ ---
179
+
180
+ ### `models`
181
+
182
+ ```bash
183
+ android-localise models # all providers
184
+ android-localise models --provider openai # one provider
185
+ ```
186
+
187
+ Lists every available model and fallback for each provider.
188
+
189
+ ---
190
+
191
+ ## Providers
192
+
193
+ By default the tool uses Gemini with `gemini-2.5-flash`. You can switch providers with `--provider` and optionally pin a specific model with `--model`.
194
+
195
+ | Provider | Default model | Fallbacks | API key env var |
196
+ |---|---|---|---|
197
+ | `gemini` _(default)_ | `gemini-2.5-flash` | `gemini-2.0-flash` → `gemini-1.5-flash` → `gemini-1.5-pro` | `GEMINI_API_KEY` |
198
+ | `openai` | `gpt-4o-mini` | `gpt-4o` → `gpt-3.5-turbo` | `OPENAI_API_KEY` |
199
+ | `anthropic` | `claude-3-5-haiku-latest` | `claude-3-5-sonnet-latest` → `claude-3-opus-latest` | `ANTHROPIC_API_KEY` |
200
+ | `custom` | set with `--model` | none | — |
201
+
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.
203
+
204
+ **Using OpenAI:**
205
+ ```bash
206
+ android-localise translate --provider openai --api-key YOUR_KEY
207
+ android-localise translate --provider openai --model gpt-4o --api-key YOUR_KEY
208
+ ```
209
+
210
+ **Using Anthropic:**
211
+ ```bash
212
+ android-localise translate --provider anthropic --api-key YOUR_KEY
213
+ ```
214
+
215
+ **Using a local model (no API key needed):**
216
+ ```bash
217
+ # Ollama
218
+ android-localise translate \
219
+ --provider custom \
220
+ --base-url http://localhost:11434/v1/chat/completions \
221
+ --model llama3
222
+
223
+ # LM Studio
224
+ android-localise translate \
225
+ --provider custom \
226
+ --base-url http://localhost:1234/v1/chat/completions \
227
+ --model mistral
228
+ ```
229
+
230
+ ---
231
+
232
+ ## Environment variables
233
+
234
+ Set your API key as an env variable so you don't have to pass it every time:
235
+
236
+ ```bash
237
+ # macOS / Linux
238
+ export GEMINI_API_KEY=your_key
239
+
240
+ # Windows PowerShell
241
+ $env:GEMINI_API_KEY = "your_key"
242
+ ```
243
+
244
+ Then just run:
245
+ ```bash
246
+ android-localise translate
247
+ ```
248
+
249
+ | Variable | Used by |
250
+ |---|---|
251
+ | `GEMINI_API_KEY` | `--provider gemini` |
252
+ | `OPENAI_API_KEY` | `--provider openai` and `--provider custom` |
253
+ | `ANTHROPIC_API_KEY` | `--provider anthropic` |
254
+
255
+ ---
256
+
257
+ ## Full workflow example
258
+
259
+ ```bash
260
+ # First time setup — create locale folders
261
+ mkdir -p app/src/main/res/values-hi
262
+ mkdir -p app/src/main/res/values-es
263
+ mkdir -p app/src/main/res/values-de
264
+
265
+ # Set your key once
266
+ export GEMINI_API_KEY=your_key
267
+
268
+ # Translate, fix, verify
269
+ android-localise translate --app-context "a habit tracking app"
270
+ android-localise fix
271
+ android-localise verify
272
+
273
+ # Build your app as usual
274
+ ./gradlew assembleDebug
275
+ ```
276
+
277
+ After this, whenever you add or change strings in your English `strings.xml`, run the same three commands again. Existing translated strings will be overwritten with fresh translations.
278
+
279
+ ---
280
+
281
+ ## Roadmap
282
+
283
+ - [ ] **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.
284
+
285
+ ---
286
+
287
+ ## Contributing
288
+
289
+ Bug reports and pull requests are welcome. For larger changes, open an issue first.
290
+
291
+ ```bash
292
+ git clone https://github.com/BharathKmalviya/android-llm-localization
293
+ cd android-llm-localization
294
+ pip install -e .
295
+ ```
296
+
297
+ Releases are automated via GitHub Actions — bump the version in `pyproject.toml` and `__init__.py`, update `CHANGELOG.md`, and push to `master`.
298
+
299
+ ---
300
+
301
+ ## License
302
+
303
+ [MIT](LICENSE)
@@ -0,0 +1,275 @@
1
+ # android-llm-localization
2
+
3
+ [![PyPI version](https://img.shields.io/pypi/v/android-localisation.svg)](https://pypi.org/project/android-localisation/)
4
+ [![Python 3.8+](https://img.shields.io/badge/python-3.8+-blue.svg)](https://pypi.org/project/android-localisation/)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
6
+
7
+ Translate your Android `strings.xml` into multiple languages using AI — Gemini, OpenAI, Anthropic, or a local model via Ollama. No paid service, no CSV exports, no copy-paste.
8
+
9
+ ---
10
+
11
+ ## The problem
12
+
13
+ Localizing an Android app the usual way means exporting strings, running them through Google Translate or some dashboard, cleaning up the output, and re-importing — for every language, every update. It's slow, error-prone, and the translations often feel robotic.
14
+
15
+ This tool does it differently. It reads your `strings.xml`, sends it to an LLM with context about your app, and writes the translated files directly into your project. The model understands UI language, keeps format specifiers intact, and produces natural-sounding output rather than word-for-word translations.
16
+
17
+ ---
18
+
19
+ ## Installation
20
+
21
+ ```bash
22
+ pip install android-localisation
23
+ ```
24
+
25
+ Requires Python 3.8+. No other dependencies.
26
+
27
+ ---
28
+
29
+ ## Quick start
30
+
31
+ ```bash
32
+ # Step 1 — translate
33
+ android-localise translate --api-key YOUR_GEMINI_KEY
34
+
35
+ # Step 2 — fix any formatting issues the LLM may have introduced
36
+ android-localise fix
37
+
38
+ # Step 3 — verify nothing will crash at runtime
39
+ android-localise verify
40
+ ```
41
+
42
+ That's the full workflow. Run these three commands after every time you update your English strings.
43
+
44
+ ---
45
+
46
+ ## What happens when you run translate
47
+
48
+ When you run `android-localise translate --api-key YOUR_KEY`, here's exactly what it does:
49
+
50
+ 1. Looks for `app/src/main/res/values/strings.xml` — this is your English source
51
+ 2. If `--languages` is provided, creates any missing `values-<lang>/` folders automatically. Otherwise scans the `res/` directory for existing `values-*` folders
52
+ 3. For each locale, if `strings.xml` doesn't exist it creates the file first, then sends your full English XML to the LLM with a prompt that instructs it to translate naturally, preserve all XML structure, and never touch format specifiers like `%1$s` or `%d`
53
+ 4. Writes the translated `strings.xml` directly into each locale folder
54
+ 5. Waits 5 seconds between each language request to avoid hitting API rate limits
55
+
56
+ **Defaults used when you don't specify anything:**
57
+
58
+ | What | Default |
59
+ |---|---|
60
+ | Provider | Gemini |
61
+ | Model | `gemini-2.5-flash` |
62
+ | Source directory | `app/src/main/res` |
63
+ | Delay between requests | 5 seconds |
64
+ | App context | none (generic prompt) |
65
+
66
+ Nothing is modified unless the translation comes back with valid XML. If a request fails, that language is skipped and logged — other languages continue.
67
+
68
+ ---
69
+
70
+ ## Setup
71
+
72
+ The only requirement is that `app/src/main/res/values/strings.xml` exists — your English source file.
73
+
74
+ For target languages, you have two options:
75
+
76
+ **Option A — let the tool create everything:**
77
+ ```bash
78
+ android-localise translate --api-key YOUR_KEY --languages hi,es,fr,de
79
+ ```
80
+ This creates `values-hi/`, `values-es/`, `values-fr/`, `values-de/` folders and their `strings.xml` files automatically, then translates into each one.
81
+
82
+ **Option B — pre-create folders yourself:**
83
+ ```
84
+ app/src/main/res/
85
+ ├── values/ ← your English source (must exist)
86
+ │ └── strings.xml
87
+ ├── values-hi/ ← empty folder is fine
88
+ ├── values-es/
89
+ └── values-fr/
90
+ ```
91
+ Run `android-localise translate --api-key YOUR_KEY` and it picks up any `values-*` folder it finds, creating `strings.xml` inside each one if it doesn't exist yet.
92
+
93
+ **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.
94
+
95
+ ---
96
+
97
+ ## Commands
98
+
99
+ ### `translate`
100
+
101
+ ```bash
102
+ android-localise translate --api-key YOUR_KEY
103
+ ```
104
+
105
+ Add `--app-context` with a one-line description of your app. This meaningfully improves translation quality — the model knows whether "record" means a music track, a health log, or a database entry:
106
+
107
+ ```bash
108
+ android-localise translate \
109
+ --api-key YOUR_KEY \
110
+ --app-context "a workout tracking app for gym beginners"
111
+ ```
112
+
113
+ **All flags:**
114
+
115
+ | Flag | What it does | Default |
116
+ |---|---|---|
117
+ | `--api-key` | Your API key | reads from env var |
118
+ | `--provider` | Which AI to use: `gemini` `openai` `anthropic` `custom` | `gemini` |
119
+ | `--model` | Specific model to use | see [Providers](#providers) |
120
+ | `--languages` | Comma-separated language codes — creates folders and files automatically | — |
121
+ | `--app-context` | One-line description of your app | — |
122
+ | `--res-dir` | Path to your `res/` folder | `app/src/main/res` |
123
+ | `--base-url` | API endpoint for local/custom providers | — |
124
+ | `--sleep` | Seconds to wait between language requests | `5.0` |
125
+
126
+ ---
127
+
128
+ ### `fix`
129
+
130
+ ```bash
131
+ android-localise fix
132
+ ```
133
+
134
+ LLMs occasionally produce output that looks correct but breaks the Android build — curly apostrophes (`'`) instead of escaped ones (`\'`), unescaped double quotes, or mangled `%` signs. This command scans every translated `strings.xml` and corrects these silently.
135
+
136
+ Always run this before `verify` and before building.
137
+
138
+ ---
139
+
140
+ ### `verify`
141
+
142
+ ```bash
143
+ android-localise verify
144
+ ```
145
+
146
+ Takes every translated string that contains a format specifier (`%1$s`, `%d`, `%1$f`, etc.) and calls `String.format()` on it using Java's actual runtime. If a translated string would throw `UnknownFormatConversionException` or `MissingFormatArgumentException` in your app, this catches it before your users do.
147
+
148
+ 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.
149
+
150
+ ---
151
+
152
+ ### `models`
153
+
154
+ ```bash
155
+ android-localise models # all providers
156
+ android-localise models --provider openai # one provider
157
+ ```
158
+
159
+ Lists every available model and fallback for each provider.
160
+
161
+ ---
162
+
163
+ ## Providers
164
+
165
+ By default the tool uses Gemini with `gemini-2.5-flash`. You can switch providers with `--provider` and optionally pin a specific model with `--model`.
166
+
167
+ | Provider | Default model | Fallbacks | API key env var |
168
+ |---|---|---|---|
169
+ | `gemini` _(default)_ | `gemini-2.5-flash` | `gemini-2.0-flash` → `gemini-1.5-flash` → `gemini-1.5-pro` | `GEMINI_API_KEY` |
170
+ | `openai` | `gpt-4o-mini` | `gpt-4o` → `gpt-3.5-turbo` | `OPENAI_API_KEY` |
171
+ | `anthropic` | `claude-3-5-haiku-latest` | `claude-3-5-sonnet-latest` → `claude-3-opus-latest` | `ANTHROPIC_API_KEY` |
172
+ | `custom` | set with `--model` | none | — |
173
+
174
+ 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.
175
+
176
+ **Using OpenAI:**
177
+ ```bash
178
+ android-localise translate --provider openai --api-key YOUR_KEY
179
+ android-localise translate --provider openai --model gpt-4o --api-key YOUR_KEY
180
+ ```
181
+
182
+ **Using Anthropic:**
183
+ ```bash
184
+ android-localise translate --provider anthropic --api-key YOUR_KEY
185
+ ```
186
+
187
+ **Using a local model (no API key needed):**
188
+ ```bash
189
+ # Ollama
190
+ android-localise translate \
191
+ --provider custom \
192
+ --base-url http://localhost:11434/v1/chat/completions \
193
+ --model llama3
194
+
195
+ # LM Studio
196
+ android-localise translate \
197
+ --provider custom \
198
+ --base-url http://localhost:1234/v1/chat/completions \
199
+ --model mistral
200
+ ```
201
+
202
+ ---
203
+
204
+ ## Environment variables
205
+
206
+ Set your API key as an env variable so you don't have to pass it every time:
207
+
208
+ ```bash
209
+ # macOS / Linux
210
+ export GEMINI_API_KEY=your_key
211
+
212
+ # Windows PowerShell
213
+ $env:GEMINI_API_KEY = "your_key"
214
+ ```
215
+
216
+ Then just run:
217
+ ```bash
218
+ android-localise translate
219
+ ```
220
+
221
+ | Variable | Used by |
222
+ |---|---|
223
+ | `GEMINI_API_KEY` | `--provider gemini` |
224
+ | `OPENAI_API_KEY` | `--provider openai` and `--provider custom` |
225
+ | `ANTHROPIC_API_KEY` | `--provider anthropic` |
226
+
227
+ ---
228
+
229
+ ## Full workflow example
230
+
231
+ ```bash
232
+ # First time setup — create locale folders
233
+ mkdir -p app/src/main/res/values-hi
234
+ mkdir -p app/src/main/res/values-es
235
+ mkdir -p app/src/main/res/values-de
236
+
237
+ # Set your key once
238
+ export GEMINI_API_KEY=your_key
239
+
240
+ # Translate, fix, verify
241
+ android-localise translate --app-context "a habit tracking app"
242
+ android-localise fix
243
+ android-localise verify
244
+
245
+ # Build your app as usual
246
+ ./gradlew assembleDebug
247
+ ```
248
+
249
+ After this, whenever you add or change strings in your English `strings.xml`, run the same three commands again. Existing translated strings will be overwritten with fresh translations.
250
+
251
+ ---
252
+
253
+ ## Roadmap
254
+
255
+ - [ ] **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.
256
+
257
+ ---
258
+
259
+ ## Contributing
260
+
261
+ Bug reports and pull requests are welcome. For larger changes, open an issue first.
262
+
263
+ ```bash
264
+ git clone https://github.com/BharathKmalviya/android-llm-localization
265
+ cd android-llm-localization
266
+ pip install -e .
267
+ ```
268
+
269
+ Releases are automated via GitHub Actions — bump the version in `pyproject.toml` and `__init__.py`, update `CHANGELOG.md`, and push to `master`.
270
+
271
+ ---
272
+
273
+ ## License
274
+
275
+ [MIT](LICENSE)
@@ -2,4 +2,4 @@
2
2
  android-localisation: Zero-dependency Android strings.xml translation using LLMs.
3
3
  """
4
4
 
5
- __version__ = "1.0.1"
5
+ __version__ = "1.0.2"
@@ -31,6 +31,7 @@ def main():
31
31
  translate_parser.add_argument("--base-url", help="Custom OpenAI-compatible endpoint URL (required for 'custom' provider)")
32
32
  translate_parser.add_argument("--app-context", help="Short description of your app for better translations")
33
33
  translate_parser.add_argument("--sleep", type=float, default=5.0, help="Seconds between API requests (default: 5.0)")
34
+ translate_parser.add_argument("--languages", help="Comma-separated language codes, e.g. hi,es,fr,de — creates folders and strings.xml automatically")
34
35
 
35
36
  # --- fix ---
36
37
  fix_parser = subparsers.add_parser("fix", help="Fix XML escaping issues in translated strings.xml files")
@@ -42,6 +42,27 @@ def get_target_directories(res_dir):
42
42
  return sorted(dirs)
43
43
 
44
44
 
45
+ def ensure_locale_dirs(res_dir, languages):
46
+ """
47
+ Creates values-<lang> directories for each language code in the list.
48
+ Returns the list of folder names created or already existing.
49
+ """
50
+ created = []
51
+ for lang in languages:
52
+ lang = lang.strip()
53
+ if not lang:
54
+ continue
55
+ folder = f"values-{lang}" if not lang.startswith("values-") else lang
56
+ folder_path = os.path.join(res_dir, folder)
57
+ if not os.path.exists(folder_path):
58
+ os.makedirs(folder_path, exist_ok=True)
59
+ print(f"📁 Created {folder}/")
60
+ created.append(folder)
61
+ else:
62
+ created.append(folder)
63
+ return created
64
+
65
+
45
66
  def read_source_xml(source_path):
46
67
  with open(source_path, "r", encoding="utf-8") as f:
47
68
  return f.read()
@@ -218,6 +239,7 @@ def _parse_args(args=None):
218
239
  parser.add_argument("--base-url")
219
240
  parser.add_argument("--app-context")
220
241
  parser.add_argument("--sleep", type=float, default=5.0)
242
+ parser.add_argument("--languages", help="Comma-separated language codes to translate into, e.g. hi,es,fr,de. Creates folders automatically if they don't exist.")
221
243
  return parser.parse_args(args)
222
244
 
223
245
 
@@ -267,10 +289,18 @@ def main(args=None):
267
289
  return
268
290
 
269
291
  source_xml = read_source_xml(source_strings_xml)
270
- target_dirs = get_target_directories(res_dir)
292
+
293
+ # Build target directory list — from --languages flag or by scanning res_dir
294
+ if args.languages:
295
+ lang_codes = [l.strip() for l in args.languages.split(",") if l.strip()]
296
+ target_dirs = ensure_locale_dirs(res_dir, lang_codes)
297
+ else:
298
+ target_dirs = get_target_directories(res_dir)
271
299
 
272
300
  if not target_dirs:
273
- print(f"⚠️ No values-* localized directories found in {res_dir}")
301
+ print(f"⚠️ No locale directories found in {res_dir}.")
302
+ print(" Either create values-<lang>/ folders manually, or use --languages to specify them:")
303
+ print(" Example: android-localise translate --languages hi,es,fr,de --api-key YOUR_KEY")
274
304
  return
275
305
 
276
306
  print(f"🌍 Found {len(target_dirs)} language directories.")
@@ -281,7 +311,12 @@ def main(args=None):
281
311
 
282
312
  for folder in target_dirs:
283
313
  target_path = os.path.join(res_dir, folder, "strings.xml")
284
- print(f"⏳ Translating for {folder}...")
314
+ is_new_file = not os.path.exists(target_path)
315
+
316
+ if is_new_file:
317
+ print(f"⏳ [{folder}] No strings.xml found — creating and translating...")
318
+ else:
319
+ print(f"⏳ [{folder}] Updating existing strings.xml...")
285
320
 
286
321
  translated_xml, used_model = translate_xml(
287
322
  actual_provider, api_key, model, source_xml,
@@ -293,7 +328,8 @@ def main(args=None):
293
328
  with open(target_path, "w", encoding="utf-8") as f:
294
329
  f.write(translated_xml)
295
330
  suffix = f" (via {used_model})" if used_model != model else ""
296
- print(f"✅ Saved translated strings.xml to {folder}{suffix}")
331
+ action = "Created" if is_new_file else "Updated"
332
+ print(f"✅ {action} {folder}/strings.xml{suffix}")
297
333
  else:
298
334
  print(f"⚠️ Failed or got invalid XML for {folder}. Skipping.")
299
335