android-localisation 1.1.2__tar.gz → 1.1.3__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.1.2/android_localisation.egg-info → android_localisation-1.1.3}/PKG-INFO +51 -1
- {android_localisation-1.1.2 → android_localisation-1.1.3}/README.md +50 -0
- {android_localisation-1.1.2 → android_localisation-1.1.3}/android_localisation/__init__.py +1 -1
- android_localisation-1.1.3/android_localisation/__main__.py +6 -0
- android_localisation-1.1.3/android_localisation/cli.py +191 -0
- {android_localisation-1.1.2 → android_localisation-1.1.3}/android_localisation/fix.py +2 -2
- android_localisation-1.1.3/android_localisation/setup_path.py +104 -0
- {android_localisation-1.1.2 → android_localisation-1.1.3}/android_localisation/translate.py +12 -9
- {android_localisation-1.1.2 → android_localisation-1.1.3}/android_localisation/verify.py +2 -2
- {android_localisation-1.1.2 → android_localisation-1.1.3/android_localisation.egg-info}/PKG-INFO +51 -1
- {android_localisation-1.1.2 → android_localisation-1.1.3}/android_localisation.egg-info/SOURCES.txt +2 -0
- {android_localisation-1.1.2 → android_localisation-1.1.3}/pyproject.toml +1 -1
- android_localisation-1.1.2/android_localisation/cli.py +0 -104
- {android_localisation-1.1.2 → android_localisation-1.1.3}/LICENSE +0 -0
- {android_localisation-1.1.2 → android_localisation-1.1.3}/MANIFEST.in +0 -0
- {android_localisation-1.1.2 → android_localisation-1.1.3}/android_localisation/java/VerifyStrings.java +0 -0
- {android_localisation-1.1.2 → android_localisation-1.1.3}/android_localisation/resources.py +0 -0
- {android_localisation-1.1.2 → android_localisation-1.1.3}/android_localisation/updates.py +0 -0
- {android_localisation-1.1.2 → android_localisation-1.1.3}/android_localisation.egg-info/dependency_links.txt +0 -0
- {android_localisation-1.1.2 → android_localisation-1.1.3}/android_localisation.egg-info/entry_points.txt +0 -0
- {android_localisation-1.1.2 → android_localisation-1.1.3}/android_localisation.egg-info/top_level.txt +0 -0
- {android_localisation-1.1.2 → android_localisation-1.1.3}/setup.cfg +0 -0
{android_localisation-1.1.2/android_localisation.egg-info → android_localisation-1.1.3}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: android-localisation
|
|
3
|
-
Version: 1.1.
|
|
3
|
+
Version: 1.1.3
|
|
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
|
|
@@ -56,6 +56,30 @@ Requires Python 3.8+. No other dependencies.
|
|
|
56
56
|
|
|
57
57
|
CLI output uses UTF-8, including when redirected to a file or pipe on Windows.
|
|
58
58
|
|
|
59
|
+
### Windows PATH setup
|
|
60
|
+
|
|
61
|
+
If PowerShell cannot find `android-localise`, run this once using the same Python
|
|
62
|
+
that installed the package:
|
|
63
|
+
|
|
64
|
+
```powershell
|
|
65
|
+
python -m android_localisation setup-path
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
This detects the installed Scripts folder and adds it to your **user PATH**,
|
|
69
|
+
preserving existing entries and avoiding duplicates. No administrator access is
|
|
70
|
+
required. Close and reopen your terminal application afterward. Normal wheel
|
|
71
|
+
installation with `pip` does not run this setup automatically, and existing
|
|
72
|
+
PowerShell sessions cannot have their environment changed by the child Python
|
|
73
|
+
process. Virtual environments should be activated instead; their Scripts folders
|
|
74
|
+
are not persisted in user PATH.
|
|
75
|
+
|
|
76
|
+
The CLI also works immediately through Python on any platform:
|
|
77
|
+
|
|
78
|
+
```powershell
|
|
79
|
+
python -m android_localisation --help
|
|
80
|
+
python -m android_localisation translate --api-key YOUR_KEY
|
|
81
|
+
```
|
|
82
|
+
|
|
59
83
|
### Update notices
|
|
60
84
|
|
|
61
85
|
Interactive `android-localise` commands check PyPI for a newer stable release in
|
|
@@ -148,6 +172,20 @@ Run `android-localise translate --api-key YOUR_KEY` and it picks up locale folde
|
|
|
148
172
|
|
|
149
173
|
## Commands
|
|
150
174
|
|
|
175
|
+
Run `android-localise` or `android-localise --help` to see every command and the
|
|
176
|
+
typical workflow. Each command has detailed help with its options and examples:
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
android-localise translate --help
|
|
180
|
+
android-localise fix --help
|
|
181
|
+
android-localise verify --help
|
|
182
|
+
android-localise models --help
|
|
183
|
+
python -m android_localisation setup-path --help
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
Use `android-localise --version` to check the installed version. Help/version
|
|
187
|
+
requests do not call providers, check for updates or modify files/PATH.
|
|
188
|
+
|
|
151
189
|
### `translate`
|
|
152
190
|
|
|
153
191
|
```bash
|
|
@@ -234,6 +272,18 @@ Lists this CLI's configured defaults and automatic fallbacks, not the provider's
|
|
|
234
272
|
|
|
235
273
|
---
|
|
236
274
|
|
|
275
|
+
### `setup-path` (Windows)
|
|
276
|
+
|
|
277
|
+
```powershell
|
|
278
|
+
python -m android_localisation setup-path
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
Adds the installed Scripts directory to Windows user PATH. See
|
|
282
|
+
[Windows PATH setup](#windows-path-setup) for terminal restart and virtual
|
|
283
|
+
environment behavior. All commands can also run through `python -m android_localisation`.
|
|
284
|
+
|
|
285
|
+
---
|
|
286
|
+
|
|
237
287
|
## Providers
|
|
238
288
|
|
|
239
289
|
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.
|
|
@@ -28,6 +28,30 @@ Requires Python 3.8+. No other dependencies.
|
|
|
28
28
|
|
|
29
29
|
CLI output uses UTF-8, including when redirected to a file or pipe on Windows.
|
|
30
30
|
|
|
31
|
+
### Windows PATH setup
|
|
32
|
+
|
|
33
|
+
If PowerShell cannot find `android-localise`, run this once using the same Python
|
|
34
|
+
that installed the package:
|
|
35
|
+
|
|
36
|
+
```powershell
|
|
37
|
+
python -m android_localisation setup-path
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
This detects the installed Scripts folder and adds it to your **user PATH**,
|
|
41
|
+
preserving existing entries and avoiding duplicates. No administrator access is
|
|
42
|
+
required. Close and reopen your terminal application afterward. Normal wheel
|
|
43
|
+
installation with `pip` does not run this setup automatically, and existing
|
|
44
|
+
PowerShell sessions cannot have their environment changed by the child Python
|
|
45
|
+
process. Virtual environments should be activated instead; their Scripts folders
|
|
46
|
+
are not persisted in user PATH.
|
|
47
|
+
|
|
48
|
+
The CLI also works immediately through Python on any platform:
|
|
49
|
+
|
|
50
|
+
```powershell
|
|
51
|
+
python -m android_localisation --help
|
|
52
|
+
python -m android_localisation translate --api-key YOUR_KEY
|
|
53
|
+
```
|
|
54
|
+
|
|
31
55
|
### Update notices
|
|
32
56
|
|
|
33
57
|
Interactive `android-localise` commands check PyPI for a newer stable release in
|
|
@@ -120,6 +144,20 @@ Run `android-localise translate --api-key YOUR_KEY` and it picks up locale folde
|
|
|
120
144
|
|
|
121
145
|
## Commands
|
|
122
146
|
|
|
147
|
+
Run `android-localise` or `android-localise --help` to see every command and the
|
|
148
|
+
typical workflow. Each command has detailed help with its options and examples:
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
android-localise translate --help
|
|
152
|
+
android-localise fix --help
|
|
153
|
+
android-localise verify --help
|
|
154
|
+
android-localise models --help
|
|
155
|
+
python -m android_localisation setup-path --help
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Use `android-localise --version` to check the installed version. Help/version
|
|
159
|
+
requests do not call providers, check for updates or modify files/PATH.
|
|
160
|
+
|
|
123
161
|
### `translate`
|
|
124
162
|
|
|
125
163
|
```bash
|
|
@@ -206,6 +244,18 @@ Lists this CLI's configured defaults and automatic fallbacks, not the provider's
|
|
|
206
244
|
|
|
207
245
|
---
|
|
208
246
|
|
|
247
|
+
### `setup-path` (Windows)
|
|
248
|
+
|
|
249
|
+
```powershell
|
|
250
|
+
python -m android_localisation setup-path
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
Adds the installed Scripts directory to Windows user PATH. See
|
|
254
|
+
[Windows PATH setup](#windows-path-setup) for terminal restart and virtual
|
|
255
|
+
environment behavior. All commands can also run through `python -m android_localisation`.
|
|
256
|
+
|
|
257
|
+
---
|
|
258
|
+
|
|
209
259
|
## Providers
|
|
210
260
|
|
|
211
261
|
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.
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Unified CLI entry point for android-localisation.
|
|
3
|
+
|
|
4
|
+
Usage:
|
|
5
|
+
android-localise translate --api-key KEY
|
|
6
|
+
android-localise fix
|
|
7
|
+
android-localise verify
|
|
8
|
+
android-localise models
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import argparse
|
|
12
|
+
import sys
|
|
13
|
+
from android_localisation import __version__
|
|
14
|
+
|
|
15
|
+
|
|
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")
|
|
20
|
+
parser = argparse.ArgumentParser(
|
|
21
|
+
prog="android-localise",
|
|
22
|
+
description="Zero-dependency Android strings.xml localization using LLMs.",
|
|
23
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
24
|
+
epilog="""Typical workflow:
|
|
25
|
+
android-localise translate --languages hi,es --app-context "a notes app"
|
|
26
|
+
android-localise fix
|
|
27
|
+
android-localise verify
|
|
28
|
+
|
|
29
|
+
Other examples:
|
|
30
|
+
android-localise translate --languages hi --missing-only --dry-run
|
|
31
|
+
android-localise models --provider openai
|
|
32
|
+
python -m android_localisation setup-path (Windows, one-time)
|
|
33
|
+
|
|
34
|
+
Use android-localise COMMAND --help for flags, defaults and examples.
|
|
35
|
+
All commands also work with: python -m android_localisation COMMAND
|
|
36
|
+
Keys: GEMINI_API_KEY, OPENAI_API_KEY, ANTHROPIC_API_KEY, or API_KEY.
|
|
37
|
+
Update notices: set ANDROID_LOCALISE_NO_UPDATE_CHECK=1 to disable them.""",
|
|
38
|
+
)
|
|
39
|
+
parser.add_argument("--version", action="version", version=f"android-localisation {__version__}")
|
|
40
|
+
|
|
41
|
+
subparsers = parser.add_subparsers(dest="command", metavar="COMMAND", title="commands")
|
|
42
|
+
subparsers.required = True
|
|
43
|
+
|
|
44
|
+
# --- translate ---
|
|
45
|
+
translate_parser = subparsers.add_parser(
|
|
46
|
+
"translate", help="Translate strings.xml into selected or existing locales",
|
|
47
|
+
description="""Translate values/strings.xml using the selected provider and app context.
|
|
48
|
+
Output is validated before saving. Existing locale files are refreshed by
|
|
49
|
+
default; --missing-only preserves existing resources and fills missing ones.""",
|
|
50
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
51
|
+
epilog="""Examples:
|
|
52
|
+
android-localise translate --languages hi,es --app-context "a notes app"
|
|
53
|
+
android-localise translate --provider openai --res-dir path/to/res
|
|
54
|
+
android-localise translate --languages hi --missing-only --dry-run
|
|
55
|
+
android-localise translate --provider custom --model YOUR_LOCAL_MODEL --base-url http://localhost:11434/v1/chat/completions
|
|
56
|
+
|
|
57
|
+
Source: RES_DIR/values/strings.xml. Existing locale folders are used when
|
|
58
|
+
--languages is omitted. --dry-run may incur API charges.
|
|
59
|
+
Exit codes: 0 success, 1 setup/API/validation/save failure, 2 invalid arguments.
|
|
60
|
+
Use android-localise models to see current defaults and fallbacks.""",
|
|
61
|
+
)
|
|
62
|
+
translate_parser.add_argument("--res-dir", default="app/src/main/res", help="Path to the Android res/ directory (default: app/src/main/res)")
|
|
63
|
+
translate_parser.add_argument("--provider", choices=["gemini", "openai", "anthropic", "custom"], default="gemini", help="AI provider (default: gemini)")
|
|
64
|
+
translate_parser.add_argument("--model", help="Pin any supported model and disable fallbacks (default: provider default; see models)")
|
|
65
|
+
translate_parser.add_argument("--api-key", help="API key, or set GEMINI_API_KEY / OPENAI_API_KEY / ANTHROPIC_API_KEY / API_KEY")
|
|
66
|
+
translate_parser.add_argument("--base-url", help="Custom OpenAI-compatible endpoint URL (required for 'custom' provider)")
|
|
67
|
+
translate_parser.add_argument("--app-context", help="Short description of your app for better translations")
|
|
68
|
+
translate_parser.add_argument("--sleep", type=float, default=5.0, help="Seconds between API requests (default: 5.0)")
|
|
69
|
+
from android_localisation.translate import DEFAULT_API_TIMEOUT, MAX_TIMEOUT_RETRIES
|
|
70
|
+
translate_parser.add_argument(
|
|
71
|
+
"--timeout", type=float, default=DEFAULT_API_TIMEOUT,
|
|
72
|
+
help=f"Seconds to wait for each API response, up to {MAX_TIMEOUT_RETRIES + 1} attempts on timeout (default: {DEFAULT_API_TIMEOUT})",
|
|
73
|
+
)
|
|
74
|
+
translate_parser.add_argument("--languages", help="Comma-separated locales (hi,es-rES,b+zh+Hans); folders are created after valid output")
|
|
75
|
+
translate_parser.add_argument("--missing-only", action="store_true", help="Translate missing resources while retaining existing translations")
|
|
76
|
+
translate_parser.add_argument("--dry-run", action="store_true", help="Generate and validate translations, then show a diff without writing files (API usage applies)")
|
|
77
|
+
|
|
78
|
+
# --- fix ---
|
|
79
|
+
fix_parser = subparsers.add_parser(
|
|
80
|
+
"fix", help="Repair apostrophe and percent escaping in locale strings",
|
|
81
|
+
description="""Repair escaping in locale <string> text and save validated XML atomically.
|
|
82
|
+
Skips formatted=false and translatable=false strings. Does not repair
|
|
83
|
+
malformed XML, double quotes, string-array items or plural items.""",
|
|
84
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
85
|
+
epilog="""Examples:
|
|
86
|
+
android-localise fix
|
|
87
|
+
android-localise fix --res-dir path/to/res
|
|
88
|
+
|
|
89
|
+
Run verify and your Android build afterward. Exit code 1 reports failures.""",
|
|
90
|
+
)
|
|
91
|
+
fix_parser.add_argument("--res-dir", default="app/src/main/res", help="Path to the Android res/ directory (default: app/src/main/res)")
|
|
92
|
+
|
|
93
|
+
# --- verify ---
|
|
94
|
+
verify_parser = subparsers.add_parser(
|
|
95
|
+
"verify", help="Check XML resources and Java format arguments (requires JDK)",
|
|
96
|
+
description="""Compare localized strings.xml files with values/strings.xml, then run
|
|
97
|
+
Java formatting checks for strings, arrays and plurals. Checks cover XML,
|
|
98
|
+
resource coverage, protected content, attributes, markup and format arguments.
|
|
99
|
+
Requires java and javac on PATH; does not replace an Android build or review.""",
|
|
100
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
101
|
+
epilog="""Examples:
|
|
102
|
+
android-localise verify
|
|
103
|
+
android-localise verify --res-dir path/to/res
|
|
104
|
+
|
|
105
|
+
Exit code 0 means checks passed; 1 reports resource, Java or setup failures.""",
|
|
106
|
+
)
|
|
107
|
+
verify_parser.add_argument("--res-dir", default="app/src/main/res", help="Path to the Android res/ directory (default: app/src/main/res)")
|
|
108
|
+
|
|
109
|
+
# --- models ---
|
|
110
|
+
models_parser = subparsers.add_parser(
|
|
111
|
+
"models", help="List configured model defaults and fallbacks",
|
|
112
|
+
description="""List this CLI release's provider defaults and automatic fallback models.
|
|
113
|
+
Use translate --model to pin a model, including models not in this list.
|
|
114
|
+
Custom/local providers require an explicit --model.""",
|
|
115
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
116
|
+
epilog="""Examples:
|
|
117
|
+
android-localise models
|
|
118
|
+
android-localise models --provider openai""",
|
|
119
|
+
)
|
|
120
|
+
models_parser.add_argument("--provider", choices=["gemini", "openai", "anthropic"], default=None,
|
|
121
|
+
help="Filter by provider (shows all if not set)")
|
|
122
|
+
|
|
123
|
+
subparsers.add_parser(
|
|
124
|
+
"setup-path", help="Add the installed Scripts folder to Windows user PATH",
|
|
125
|
+
description="""One-time Windows setup for an installed CLI that PowerShell cannot find.
|
|
126
|
+
Adds the installed Scripts folder to user PATH without administrator access,
|
|
127
|
+
preserving entries and avoiding duplicates. Virtual environments use activation.
|
|
128
|
+
Ordinary commands and pip installation do not change PATH.""",
|
|
129
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
130
|
+
epilog="""Run with the same Python that installed the package:
|
|
131
|
+
python -m android_localisation setup-path
|
|
132
|
+
|
|
133
|
+
Close and reopen your terminal application afterward. Until then, use:
|
|
134
|
+
python -m android_localisation --help
|
|
135
|
+
|
|
136
|
+
Exit code 1 reports unsupported platforms, virtual environments or setup failures.""",
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
if args is None or isinstance(args, list):
|
|
140
|
+
argv = sys.argv[1:] if args is None else args
|
|
141
|
+
if not argv:
|
|
142
|
+
parser.print_help()
|
|
143
|
+
return 0
|
|
144
|
+
args = parser.parse_args(argv)
|
|
145
|
+
|
|
146
|
+
from android_localisation.updates import start_update_check, show_update_notice
|
|
147
|
+
update_state = start_update_check()
|
|
148
|
+
try:
|
|
149
|
+
return _run_command(args)
|
|
150
|
+
finally:
|
|
151
|
+
show_update_notice(update_state)
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def _run_command(args):
|
|
155
|
+
if args.command == "setup-path":
|
|
156
|
+
from android_localisation.setup_path import main as run
|
|
157
|
+
return run(args)
|
|
158
|
+
|
|
159
|
+
elif args.command == "translate":
|
|
160
|
+
from android_localisation.translate import main as run
|
|
161
|
+
return run(args)
|
|
162
|
+
|
|
163
|
+
elif args.command == "fix":
|
|
164
|
+
from android_localisation.fix import main as run
|
|
165
|
+
return run(args)
|
|
166
|
+
|
|
167
|
+
elif args.command == "verify":
|
|
168
|
+
from android_localisation.verify import main as run
|
|
169
|
+
return run(args)
|
|
170
|
+
|
|
171
|
+
elif args.command == "models":
|
|
172
|
+
from android_localisation.translate import PROVIDER_MODELS
|
|
173
|
+
providers = [args.provider] if args.provider else ["gemini", "openai", "anthropic"]
|
|
174
|
+
print()
|
|
175
|
+
for p in providers:
|
|
176
|
+
models = PROVIDER_MODELS.get(p, [])
|
|
177
|
+
print(f" {p.upper()}")
|
|
178
|
+
for i, m in enumerate(models):
|
|
179
|
+
tag = " (default)" if i == 0 else f" (fallback {i})" if i < len(models) - 1 else " (fallback)"
|
|
180
|
+
print(f" {'→' if i == 0 else ' '} {m}{tag}")
|
|
181
|
+
print()
|
|
182
|
+
print(" CUSTOM (Ollama, LM Studio, etc.)")
|
|
183
|
+
print(" → Any model name your local server supports (must use --model)")
|
|
184
|
+
print()
|
|
185
|
+
print(" Tip: use --model to pick any model, e.g:")
|
|
186
|
+
print(" android-localise translate --provider openai --model gpt-6-luna --api-key KEY")
|
|
187
|
+
print()
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
if __name__ == "__main__":
|
|
191
|
+
raise SystemExit(main())
|
|
@@ -46,8 +46,8 @@ def _fix_text(text):
|
|
|
46
46
|
|
|
47
47
|
|
|
48
48
|
def _parse_args(args=None):
|
|
49
|
-
parser = argparse.ArgumentParser(description="
|
|
50
|
-
parser.add_argument("--res-dir", default="app/src/main/res", help="Path to the Android res/ directory")
|
|
49
|
+
parser = argparse.ArgumentParser(description="Repair apostrophe and percent escaping in locale string text; requires parseable XML. Skips formatted=false/translatable=false strings and does not repair arrays or plurals.")
|
|
50
|
+
parser.add_argument("--res-dir", default="app/src/main/res", help="Path to the Android res/ directory (default: app/src/main/res)")
|
|
51
51
|
return parser.parse_args(args)
|
|
52
52
|
|
|
53
53
|
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"""One-time Windows user PATH setup; never run implicitly during imports."""
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import os
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
import sys
|
|
7
|
+
import sysconfig
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def _script_directory():
|
|
11
|
+
directories = [sysconfig.get_path("scripts")]
|
|
12
|
+
if "nt_user" in sysconfig.get_scheme_names():
|
|
13
|
+
user_scripts = sysconfig.get_path("scripts", scheme="nt_user")
|
|
14
|
+
user_library = sysconfig.get_path("purelib", scheme="nt_user")
|
|
15
|
+
try:
|
|
16
|
+
if os.path.normcase(os.path.commonpath([os.path.abspath(__file__), user_library])) == os.path.normcase(user_library):
|
|
17
|
+
directories.insert(0, user_scripts)
|
|
18
|
+
else:
|
|
19
|
+
directories.append(user_scripts)
|
|
20
|
+
except ValueError:
|
|
21
|
+
directories.append(user_scripts)
|
|
22
|
+
for directory in directories:
|
|
23
|
+
if (Path(directory) / "android-localise.exe").is_file():
|
|
24
|
+
return str(Path(directory).resolve())
|
|
25
|
+
raise ValueError(
|
|
26
|
+
"Cannot find android-localise.exe for this Python. Reinstall with: "
|
|
27
|
+
"python -m pip install --upgrade android-localisation"
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _contains_directory(path, directory):
|
|
32
|
+
def normalized(value):
|
|
33
|
+
return os.path.normcase(os.path.normpath(os.path.expandvars(value.strip().strip('"'))))
|
|
34
|
+
return any(entry.strip() and normalized(entry) == normalized(directory)
|
|
35
|
+
for entry in path.split(";"))
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _add_user_path(directory):
|
|
39
|
+
import winreg
|
|
40
|
+
if ";" in directory:
|
|
41
|
+
raise ValueError("The Scripts directory contains a semicolon and cannot be added to PATH.")
|
|
42
|
+
with winreg.CreateKeyEx(winreg.HKEY_CURRENT_USER, "Environment", 0,
|
|
43
|
+
winreg.KEY_QUERY_VALUE | winreg.KEY_SET_VALUE) as key:
|
|
44
|
+
try:
|
|
45
|
+
existing, kind = winreg.QueryValueEx(key, "Path")
|
|
46
|
+
except FileNotFoundError:
|
|
47
|
+
existing, kind = "", winreg.REG_EXPAND_SZ
|
|
48
|
+
if not isinstance(existing, str) or kind not in (winreg.REG_SZ, winreg.REG_EXPAND_SZ):
|
|
49
|
+
raise ValueError("User PATH has an unexpected registry type; no changes made.")
|
|
50
|
+
if _contains_directory(existing, directory):
|
|
51
|
+
return False
|
|
52
|
+
separator = ";" if existing and not existing.endswith(";") else ""
|
|
53
|
+
updated = existing + separator + directory
|
|
54
|
+
if len(updated) >= 32767:
|
|
55
|
+
raise ValueError("User PATH would exceed Windows' length limit; no changes made.")
|
|
56
|
+
winreg.SetValueEx(key, "Path", 0, kind, updated)
|
|
57
|
+
return True
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def _notify_environment_change():
|
|
61
|
+
# Let Windows shells refresh their environment; existing terminals still
|
|
62
|
+
# inherit their original environment until the terminal application restarts.
|
|
63
|
+
try:
|
|
64
|
+
import ctypes
|
|
65
|
+
from ctypes import wintypes
|
|
66
|
+
notify = ctypes.WinDLL("user32", use_last_error=True).SendMessageTimeoutW
|
|
67
|
+
notify.argtypes = [wintypes.HWND, wintypes.UINT, wintypes.WPARAM,
|
|
68
|
+
ctypes.c_wchar_p, wintypes.UINT, wintypes.UINT,
|
|
69
|
+
ctypes.POINTER(ctypes.c_size_t)]
|
|
70
|
+
notify.restype = wintypes.LPARAM
|
|
71
|
+
result = ctypes.c_size_t()
|
|
72
|
+
notify(0xffff, 0x001a, 0, "Environment", 2, 1000, ctypes.byref(result))
|
|
73
|
+
except Exception:
|
|
74
|
+
pass
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _parse_args(args=None):
|
|
78
|
+
return argparse.ArgumentParser(description="Add this Python's CLI Scripts directory to Windows user PATH.").parse_args(args)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def main(args=None):
|
|
82
|
+
if args is None or isinstance(args, list):
|
|
83
|
+
args = _parse_args(args)
|
|
84
|
+
if os.name != "nt":
|
|
85
|
+
print("setup-path is for Windows. You can run the CLI with: python -m android_localisation --help")
|
|
86
|
+
return 1
|
|
87
|
+
if sys.prefix != sys.base_prefix or hasattr(sys, "real_prefix"):
|
|
88
|
+
print("Virtual environment detected. Activate it to use android-localise; its temporary Scripts directory will not be added to user PATH.")
|
|
89
|
+
return 1
|
|
90
|
+
try:
|
|
91
|
+
directory = _script_directory()
|
|
92
|
+
changed = _add_user_path(directory)
|
|
93
|
+
except (OSError, ValueError) as error:
|
|
94
|
+
print("PATH setup failed: {}".format(error))
|
|
95
|
+
return 1
|
|
96
|
+
_notify_environment_change()
|
|
97
|
+
print("{}: {}".format("Added to user PATH" if changed else "Already on user PATH", directory))
|
|
98
|
+
print("Close and reopen your terminal application, then run: android-localise --help")
|
|
99
|
+
print("You can use it immediately in this terminal with: python -m android_localisation --help")
|
|
100
|
+
return 0
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
if __name__ == "__main__":
|
|
104
|
+
raise SystemExit(main())
|
|
@@ -294,17 +294,20 @@ def translate_xml(provider, api_key, model, source_xml, target_folder_name, app_
|
|
|
294
294
|
|
|
295
295
|
|
|
296
296
|
def _parse_args(args=None):
|
|
297
|
-
parser = argparse.ArgumentParser(
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
parser.add_argument("--
|
|
302
|
-
parser.add_argument("--
|
|
303
|
-
parser.add_argument("--
|
|
304
|
-
parser.add_argument("--
|
|
297
|
+
parser = argparse.ArgumentParser(
|
|
298
|
+
description="Translate values/strings.xml using LLMs; validate output before saving. Existing locale files are refreshed unless --missing-only is set.",
|
|
299
|
+
epilog="Use python -m android_localisation translate --help for workflow examples, or python -m android_localisation models for model defaults.",
|
|
300
|
+
)
|
|
301
|
+
parser.add_argument("--res-dir", default=DEFAULT_RES_DIR, help="Path to the Android res/ directory (default: app/src/main/res)")
|
|
302
|
+
parser.add_argument("--provider", choices=["gemini", "openai", "anthropic", "custom"], default="gemini", help="AI provider (default: gemini)")
|
|
303
|
+
parser.add_argument("--model", help="Pin any supported model and disable fallbacks (default: provider default; see models)")
|
|
304
|
+
parser.add_argument("--api-key", help="API key, or set GEMINI_API_KEY / OPENAI_API_KEY / ANTHROPIC_API_KEY / API_KEY")
|
|
305
|
+
parser.add_argument("--base-url", help="Custom OpenAI-compatible endpoint URL (required for 'custom' provider)")
|
|
306
|
+
parser.add_argument("--app-context", help="Short description of your app for better translations")
|
|
307
|
+
parser.add_argument("--sleep", type=float, default=5.0, help="Seconds between API requests (default: 5.0)")
|
|
305
308
|
parser.add_argument("--timeout", type=float, default=DEFAULT_API_TIMEOUT,
|
|
306
309
|
help=f"Seconds to wait for each API response, up to {MAX_TIMEOUT_RETRIES + 1} attempts on timeout (default: {DEFAULT_API_TIMEOUT})")
|
|
307
|
-
parser.add_argument("--languages", help="Comma-separated
|
|
310
|
+
parser.add_argument("--languages", help="Comma-separated locales (hi,es-rES,b+zh+Hans); folders are created after valid output")
|
|
308
311
|
parser.add_argument("--missing-only", action="store_true", help="Translate missing resources while retaining existing translations")
|
|
309
312
|
parser.add_argument("--dry-run", action="store_true", help="Generate and validate translations, then show a diff without writing files (API usage applies)")
|
|
310
313
|
return parser.parse_args(args)
|
|
@@ -11,8 +11,8 @@ from android_localisation.resources import locale_folders, parse_resources, vali
|
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
def _parse_args(args=None):
|
|
14
|
-
parser = argparse.ArgumentParser(description="
|
|
15
|
-
parser.add_argument("--res-dir", default="app/src/main/res", help="Path to the Android res/ directory")
|
|
14
|
+
parser = argparse.ArgumentParser(description="Check XML resource coverage, protected content and format arguments, then run Java formatting checks. Requires java and javac on PATH.")
|
|
15
|
+
parser.add_argument("--res-dir", default="app/src/main/res", help="Path to the Android res/ directory (default: app/src/main/res)")
|
|
16
16
|
return parser.parse_args(args)
|
|
17
17
|
|
|
18
18
|
|
{android_localisation-1.1.2 → android_localisation-1.1.3/android_localisation.egg-info}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: android-localisation
|
|
3
|
-
Version: 1.1.
|
|
3
|
+
Version: 1.1.3
|
|
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
|
|
@@ -56,6 +56,30 @@ Requires Python 3.8+. No other dependencies.
|
|
|
56
56
|
|
|
57
57
|
CLI output uses UTF-8, including when redirected to a file or pipe on Windows.
|
|
58
58
|
|
|
59
|
+
### Windows PATH setup
|
|
60
|
+
|
|
61
|
+
If PowerShell cannot find `android-localise`, run this once using the same Python
|
|
62
|
+
that installed the package:
|
|
63
|
+
|
|
64
|
+
```powershell
|
|
65
|
+
python -m android_localisation setup-path
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
This detects the installed Scripts folder and adds it to your **user PATH**,
|
|
69
|
+
preserving existing entries and avoiding duplicates. No administrator access is
|
|
70
|
+
required. Close and reopen your terminal application afterward. Normal wheel
|
|
71
|
+
installation with `pip` does not run this setup automatically, and existing
|
|
72
|
+
PowerShell sessions cannot have their environment changed by the child Python
|
|
73
|
+
process. Virtual environments should be activated instead; their Scripts folders
|
|
74
|
+
are not persisted in user PATH.
|
|
75
|
+
|
|
76
|
+
The CLI also works immediately through Python on any platform:
|
|
77
|
+
|
|
78
|
+
```powershell
|
|
79
|
+
python -m android_localisation --help
|
|
80
|
+
python -m android_localisation translate --api-key YOUR_KEY
|
|
81
|
+
```
|
|
82
|
+
|
|
59
83
|
### Update notices
|
|
60
84
|
|
|
61
85
|
Interactive `android-localise` commands check PyPI for a newer stable release in
|
|
@@ -148,6 +172,20 @@ Run `android-localise translate --api-key YOUR_KEY` and it picks up locale folde
|
|
|
148
172
|
|
|
149
173
|
## Commands
|
|
150
174
|
|
|
175
|
+
Run `android-localise` or `android-localise --help` to see every command and the
|
|
176
|
+
typical workflow. Each command has detailed help with its options and examples:
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
android-localise translate --help
|
|
180
|
+
android-localise fix --help
|
|
181
|
+
android-localise verify --help
|
|
182
|
+
android-localise models --help
|
|
183
|
+
python -m android_localisation setup-path --help
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
Use `android-localise --version` to check the installed version. Help/version
|
|
187
|
+
requests do not call providers, check for updates or modify files/PATH.
|
|
188
|
+
|
|
151
189
|
### `translate`
|
|
152
190
|
|
|
153
191
|
```bash
|
|
@@ -234,6 +272,18 @@ Lists this CLI's configured defaults and automatic fallbacks, not the provider's
|
|
|
234
272
|
|
|
235
273
|
---
|
|
236
274
|
|
|
275
|
+
### `setup-path` (Windows)
|
|
276
|
+
|
|
277
|
+
```powershell
|
|
278
|
+
python -m android_localisation setup-path
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
Adds the installed Scripts directory to Windows user PATH. See
|
|
282
|
+
[Windows PATH setup](#windows-path-setup) for terminal restart and virtual
|
|
283
|
+
environment behavior. All commands can also run through `python -m android_localisation`.
|
|
284
|
+
|
|
285
|
+
---
|
|
286
|
+
|
|
237
287
|
## Providers
|
|
238
288
|
|
|
239
289
|
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.
|
{android_localisation-1.1.2 → android_localisation-1.1.3}/android_localisation.egg-info/SOURCES.txt
RENAMED
|
@@ -3,9 +3,11 @@ MANIFEST.in
|
|
|
3
3
|
README.md
|
|
4
4
|
pyproject.toml
|
|
5
5
|
android_localisation/__init__.py
|
|
6
|
+
android_localisation/__main__.py
|
|
6
7
|
android_localisation/cli.py
|
|
7
8
|
android_localisation/fix.py
|
|
8
9
|
android_localisation/resources.py
|
|
10
|
+
android_localisation/setup_path.py
|
|
9
11
|
android_localisation/translate.py
|
|
10
12
|
android_localisation/updates.py
|
|
11
13
|
android_localisation/verify.py
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "android-localisation"
|
|
7
|
-
version = "1.1.
|
|
7
|
+
version = "1.1.3"
|
|
8
8
|
description = "Zero-dependency Android strings.xml translation and verification using LLMs (Gemini, OpenAI, Anthropic, Ollama)."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = { text = "MIT" }
|
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Unified CLI entry point for android-localisation.
|
|
3
|
-
|
|
4
|
-
Usage:
|
|
5
|
-
android-localise translate --api-key KEY
|
|
6
|
-
android-localise fix
|
|
7
|
-
android-localise verify
|
|
8
|
-
android-localise models
|
|
9
|
-
"""
|
|
10
|
-
|
|
11
|
-
import argparse
|
|
12
|
-
import sys
|
|
13
|
-
from android_localisation import __version__
|
|
14
|
-
|
|
15
|
-
|
|
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")
|
|
20
|
-
parser = argparse.ArgumentParser(
|
|
21
|
-
prog="android-localise",
|
|
22
|
-
description="Zero-dependency Android strings.xml localization using LLMs.",
|
|
23
|
-
)
|
|
24
|
-
parser.add_argument("--version", action="version", version=f"android-localisation {__version__}")
|
|
25
|
-
|
|
26
|
-
subparsers = parser.add_subparsers(dest="command", metavar="COMMAND")
|
|
27
|
-
subparsers.required = True
|
|
28
|
-
|
|
29
|
-
# --- translate ---
|
|
30
|
-
translate_parser = subparsers.add_parser("translate", help="Translate strings.xml into all locale directories")
|
|
31
|
-
translate_parser.add_argument("--res-dir", default="app/src/main/res", help="Path to the Android res/ directory (default: app/src/main/res)")
|
|
32
|
-
translate_parser.add_argument("--provider", choices=["gemini", "openai", "anthropic", "custom"], default="gemini", help="AI provider (default: gemini)")
|
|
33
|
-
translate_parser.add_argument("--model", help="Any model name supported by the provider (uses provider default if not set)")
|
|
34
|
-
translate_parser.add_argument("--api-key", help="API key (or set GEMINI_API_KEY / OPENAI_API_KEY / ANTHROPIC_API_KEY)")
|
|
35
|
-
translate_parser.add_argument("--base-url", help="Custom OpenAI-compatible endpoint URL (required for 'custom' provider)")
|
|
36
|
-
translate_parser.add_argument("--app-context", help="Short description of your app for better translations")
|
|
37
|
-
translate_parser.add_argument("--sleep", type=float, default=5.0, help="Seconds between API requests (default: 5.0)")
|
|
38
|
-
from android_localisation.translate import DEFAULT_API_TIMEOUT, MAX_TIMEOUT_RETRIES
|
|
39
|
-
translate_parser.add_argument(
|
|
40
|
-
"--timeout", type=float, default=DEFAULT_API_TIMEOUT,
|
|
41
|
-
help=f"Seconds to wait for each API response, up to {MAX_TIMEOUT_RETRIES + 1} attempts on timeout (default: {DEFAULT_API_TIMEOUT})",
|
|
42
|
-
)
|
|
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)")
|
|
46
|
-
|
|
47
|
-
# --- fix ---
|
|
48
|
-
fix_parser = subparsers.add_parser("fix", help="Fix XML escaping issues in translated strings.xml files")
|
|
49
|
-
fix_parser.add_argument("--res-dir", default="app/src/main/res", help="Path to the Android res/ directory (default: app/src/main/res)")
|
|
50
|
-
|
|
51
|
-
# --- verify ---
|
|
52
|
-
verify_parser = subparsers.add_parser("verify", help="Verify translated strings won't crash the app (requires javac)")
|
|
53
|
-
verify_parser.add_argument("--res-dir", default="app/src/main/res", help="Path to the Android res/ directory (default: app/src/main/res)")
|
|
54
|
-
|
|
55
|
-
# --- models ---
|
|
56
|
-
models_parser = subparsers.add_parser("models", help="List configured model defaults and fallbacks")
|
|
57
|
-
models_parser.add_argument("--provider", choices=["gemini", "openai", "anthropic"], default=None,
|
|
58
|
-
help="Filter by provider (shows all if not set)")
|
|
59
|
-
|
|
60
|
-
if args is None or isinstance(args, list):
|
|
61
|
-
args = parser.parse_args(args)
|
|
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):
|
|
72
|
-
if args.command == "translate":
|
|
73
|
-
from android_localisation.translate import main as run
|
|
74
|
-
return run(args)
|
|
75
|
-
|
|
76
|
-
elif args.command == "fix":
|
|
77
|
-
from android_localisation.fix import main as run
|
|
78
|
-
return run(args)
|
|
79
|
-
|
|
80
|
-
elif args.command == "verify":
|
|
81
|
-
from android_localisation.verify import main as run
|
|
82
|
-
return run(args)
|
|
83
|
-
|
|
84
|
-
elif args.command == "models":
|
|
85
|
-
from android_localisation.translate import PROVIDER_MODELS
|
|
86
|
-
providers = [args.provider] if args.provider else ["gemini", "openai", "anthropic"]
|
|
87
|
-
print()
|
|
88
|
-
for p in providers:
|
|
89
|
-
models = PROVIDER_MODELS.get(p, [])
|
|
90
|
-
print(f" {p.upper()}")
|
|
91
|
-
for i, m in enumerate(models):
|
|
92
|
-
tag = " (default)" if i == 0 else f" (fallback {i})" if i < len(models) - 1 else " (fallback)"
|
|
93
|
-
print(f" {'→' if i == 0 else ' '} {m}{tag}")
|
|
94
|
-
print()
|
|
95
|
-
print(" CUSTOM (Ollama, LM Studio, etc.)")
|
|
96
|
-
print(" → Any model name your local server supports (must use --model)")
|
|
97
|
-
print()
|
|
98
|
-
print(" Tip: use --model to pick any model, e.g:")
|
|
99
|
-
print(" android-localise translate --provider openai --model gpt-6-luna --api-key KEY")
|
|
100
|
-
print()
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
if __name__ == "__main__":
|
|
104
|
-
raise SystemExit(main())
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|