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.
Files changed (22) hide show
  1. {android_localisation-1.1.2/android_localisation.egg-info → android_localisation-1.1.3}/PKG-INFO +51 -1
  2. {android_localisation-1.1.2 → android_localisation-1.1.3}/README.md +50 -0
  3. {android_localisation-1.1.2 → android_localisation-1.1.3}/android_localisation/__init__.py +1 -1
  4. android_localisation-1.1.3/android_localisation/__main__.py +6 -0
  5. android_localisation-1.1.3/android_localisation/cli.py +191 -0
  6. {android_localisation-1.1.2 → android_localisation-1.1.3}/android_localisation/fix.py +2 -2
  7. android_localisation-1.1.3/android_localisation/setup_path.py +104 -0
  8. {android_localisation-1.1.2 → android_localisation-1.1.3}/android_localisation/translate.py +12 -9
  9. {android_localisation-1.1.2 → android_localisation-1.1.3}/android_localisation/verify.py +2 -2
  10. {android_localisation-1.1.2 → android_localisation-1.1.3/android_localisation.egg-info}/PKG-INFO +51 -1
  11. {android_localisation-1.1.2 → android_localisation-1.1.3}/android_localisation.egg-info/SOURCES.txt +2 -0
  12. {android_localisation-1.1.2 → android_localisation-1.1.3}/pyproject.toml +1 -1
  13. android_localisation-1.1.2/android_localisation/cli.py +0 -104
  14. {android_localisation-1.1.2 → android_localisation-1.1.3}/LICENSE +0 -0
  15. {android_localisation-1.1.2 → android_localisation-1.1.3}/MANIFEST.in +0 -0
  16. {android_localisation-1.1.2 → android_localisation-1.1.3}/android_localisation/java/VerifyStrings.java +0 -0
  17. {android_localisation-1.1.2 → android_localisation-1.1.3}/android_localisation/resources.py +0 -0
  18. {android_localisation-1.1.2 → android_localisation-1.1.3}/android_localisation/updates.py +0 -0
  19. {android_localisation-1.1.2 → android_localisation-1.1.3}/android_localisation.egg-info/dependency_links.txt +0 -0
  20. {android_localisation-1.1.2 → android_localisation-1.1.3}/android_localisation.egg-info/entry_points.txt +0 -0
  21. {android_localisation-1.1.2 → android_localisation-1.1.3}/android_localisation.egg-info/top_level.txt +0 -0
  22. {android_localisation-1.1.2 → android_localisation-1.1.3}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: android-localisation
3
- Version: 1.1.2
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.
@@ -2,4 +2,4 @@
2
2
  android-localisation: Zero-dependency Android strings.xml translation using LLMs.
3
3
  """
4
4
 
5
- __version__ = "1.1.2"
5
+ __version__ = "1.1.3"
@@ -0,0 +1,6 @@
1
+ """Run the CLI through Python when its console script is not on PATH."""
2
+
3
+ from android_localisation.cli import main
4
+
5
+ if __name__ == "__main__":
6
+ raise SystemExit(main())
@@ -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="Fix common string formatting issues in Android strings.xml files.")
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(description="Translate Android strings.xml using LLMs.")
298
- parser.add_argument("--res-dir", default=DEFAULT_RES_DIR)
299
- parser.add_argument("--provider", choices=["gemini", "openai", "anthropic", "custom"], default="gemini")
300
- parser.add_argument("--model", help="Any model name for the chosen provider. Uses provider default if not set.")
301
- parser.add_argument("--api-key")
302
- parser.add_argument("--base-url")
303
- parser.add_argument("--app-context")
304
- parser.add_argument("--sleep", type=float, default=5.0)
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 language codes to translate into, e.g. hi,es,fr,de. Creates folders automatically if they don't exist.")
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="Verify Android strings formatting.")
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
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: android-localisation
3
- Version: 1.1.2
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.
@@ -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.2"
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())