Skip to content

Commit dab557b

Browse files
committed
Fix CloudSyncValidatorTests for updated error messages
Update test assertions to match refactored error message format in ValidateForLink and ValidateForPush methods after Android/iOS support was added. Error messages now use a more descriptive format indicating the detected local format instead of just listing file types. Also includes documentation updates for Android/iOS support in README, docs/CONFIGURATION.md, vscode-extension/README.md, and cloud www docs.
1 parent 1d359c3 commit dab557b

6 files changed

Lines changed: 177 additions & 54 deletions

File tree

LocalizationManager.Tests/UnitTests/Cloud/CloudSyncValidatorTests.cs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ public void ValidateForPush_LocalConfigSaysResxButOnlyJsonFilesExist_CannotSync(
102102

103103
// Assert
104104
Assert.False(result.CanSync);
105-
Assert.Contains(result.Errors, e => e.Contains("lrm.json specifies format 'resx' but only .json files found"));
105+
Assert.Contains(result.Errors, e => e.Contains("lrm.json specifies format 'resx' but local files appear to be 'json'"));
106106
}
107107

108108
[Fact]
@@ -118,7 +118,7 @@ public void ValidateForPush_LocalConfigSaysJsonButOnlyResxFilesExist_CannotSync(
118118

119119
// Assert
120120
Assert.False(result.CanSync);
121-
Assert.Contains(result.Errors, e => e.Contains("lrm.json specifies format 'json' but only .resx files found"));
121+
Assert.Contains(result.Errors, e => e.Contains("lrm.json specifies format 'json' but local files appear to be 'resx'"));
122122
}
123123

124124
[Fact]
@@ -290,7 +290,7 @@ public void ValidateForLink_ResxProjectWithOnlyJsonFiles_CannotLink()
290290
// Assert
291291
Assert.False(result.CanSync);
292292
Assert.Contains(result.Errors, e => e.Contains("Cannot link to cloud project with format 'resx'"));
293-
Assert.Contains(result.Errors, e => e.Contains("no .resx files found"));
293+
Assert.Contains(result.Errors, e => e.Contains("local project uses 'json'"));
294294
}
295295

296296
[Fact]
@@ -306,7 +306,7 @@ public void ValidateForLink_JsonProjectWithOnlyResxFiles_CannotLink()
306306
// Assert
307307
Assert.False(result.CanSync);
308308
Assert.Contains(result.Errors, e => e.Contains("Cannot link to cloud project with format 'json'"));
309-
Assert.Contains(result.Errors, e => e.Contains("no .json files found"));
309+
Assert.Contains(result.Errors, e => e.Contains("local project uses 'resx'"));
310310
}
311311

312312
[Fact]
@@ -320,7 +320,8 @@ public void ValidateForLink_ProvidesConversionSuggestion()
320320
var result = _validator.ValidateForLink(remoteProject);
321321

322322
// Assert
323-
Assert.Contains(result.Errors, e => e.Contains("lrm convert"));
323+
// When formats don't match, suggests creating a new cloud project with the detected local format
324+
Assert.Contains(result.Errors, e => e.Contains("Create a new cloud project with format 'resx'"));
324325
}
325326

326327
#endregion

README.md

Lines changed: 47 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
[![GitHub Discussions](https://img.shields.io/github/discussions/nickprotop/LocalizationManager)](https://github.com/nickprotop/LocalizationManager/discussions)
1010
[![Buy Me A Coffee](https://img.shields.io/badge/Buy%20Me%20A%20Coffee-support-yellow?logo=buy-me-a-coffee&logoColor=white)](https://buymeacoffee.com/nickprotop)
1111

12-
**A powerful, Linux-native command-line tool for managing .NET `.resx` localization files with an interactive Terminal UI and Web UI.**
12+
**A powerful, cross-platform command-line tool for managing localization files with an interactive Terminal UI and Web UI. Supports .NET `.resx`, JSON, i18next, Android `strings.xml`, and iOS `.strings` formats.**
1313

1414
![LRM Demo](assets/lrm-demo.gif)
1515

@@ -68,12 +68,14 @@ Cloud platform for team-based localization management with web dashboard and CLI
6868

6969
## Why This Tool Exists
7070

71-
Managing `.resx` files for .NET localization is painful on Linux:
71+
Managing localization files is painful:
7272
- **Visual Studio** and **ResXResourceManager** are Windows-only
73-
- **Manual XML editing** is error-prone and time-consuming
73+
- **Manual XML/JSON editing** is error-prone and time-consuming
74+
- **No unified tool** for .NET, Android, iOS, and web projects
7475
- **No Linux-native tools** with interactive editing existed
7576

7677
LRM solves this by providing:
78+
- **Multi-format support** - .resx, JSON, i18next, Android, iOS
7779
- **First-class Linux support** with native binaries
7880
- **Interactive Terminal UI (TUI)** for visual editing
7981
- **Full CLI** for scripting and CI/CD automation
@@ -101,7 +103,7 @@ LRM solves this by providing:
101103
| **Self-contained** | ✅ Single binary | ❌ Needs .NET Runtime | ❌ Installer | ❌ Large install ||
102104
| **ARM64 Support** | ✅ Native |||| ✅ Any editor |
103105

104-
**LRM is the only Linux-native, CLI-first tool with an interactive TUI, Web UI, and REST API for .resx management.**
106+
**LRM is the only Linux-native, CLI-first tool with an interactive TUI, Web UI, and REST API for multi-format localization management (.resx, JSON, i18next, Android, iOS).**
105107

106108
---
107109

@@ -128,33 +130,48 @@ LRM solves this by providing:
128130
129131
---
130132
131-
## JSON Localization
133+
## Multi-Format Support
132134
133-
> **Beyond .NET:** With JSON and i18next support, LRM becomes a universal localization tool for **React, Vue, Angular, Node.js**, and any project using JSON resources.
135+
> **Beyond .NET:** LRM is a universal localization tool supporting **React, Vue, Angular, Node.js, Android, iOS**, and any project using JSON or platform-specific resources.
134136
135-
**The Killer Combo:** JSON localization + 10 translation providers = automated multilingual apps in any ecosystem.
137+
**The Killer Combo:** Multi-format support + 10 translation providers = automated multilingual apps in any ecosystem.
136138
137-
```bash
138-
# Auto-translate your React/Vue/Angular app to 5 languages
139-
lrm translate --provider google --to fr,de,es,it,ja --path ./locales
140-
```
139+
### Supported Formats
141140
142-
**Why JSON?**
143-
- **Human-readable** - Edit directly in any text editor, no special tools needed
144-
- **Git-friendly** - Clean diffs and easy merge conflict resolution
145-
- **Nested structure** - Organize keys hierarchically (`Errors.NotFound`, `Navigation.Home`)
146-
- **Universal** - Works with any framework, any language, any platform
141+
| Format | File Pattern | Use Case |
142+
|--------|-------------|----------|
143+
| **.resx** | `Resources.resx`, `Resources.fr.resx` | .NET applications |
144+
| **JSON** | `strings.json`, `strings.fr.json` | Web apps, Node.js |
145+
| **i18next** | `en.json`, `fr.json` | React, Vue, Angular |
146+
| **Android** | `res/values/strings.xml`, `res/values-fr/strings.xml` | Android apps |
147+
| **iOS** | `en.lproj/Localizable.strings`, `fr.lproj/Localizable.strings` | iOS/macOS apps |
147148

148-
**Two Formats Supported:**
149-
- **Standard JSON** - `.json` files with nested keys and CLDR plural markers (`_plural: true`)
150-
- **i18next** - Full compatibility with suffix-based plurals (`items_one`, `items_other`) for React, Vue, Angular, Node.js
149+
### Quick Start by Platform
151150

152-
**For .NET Developers:**
153-
The `LocalizationManager.JsonLocalization` NuGet package is a drop-in replacement for `IStringLocalizer<T>` - migrate from .resx with one command:
154151
```bash
155-
lrm convert --from resx --to json --path ./Resources
152+
# Initialize Android project
153+
lrm init --format android --languages es,fr,de
154+
155+
# Initialize iOS project
156+
lrm init --format ios --languages es,fr,de
157+
158+
# Auto-translate your React/Vue/Angular app
159+
lrm translate --provider google --to fr,de,es,it,ja --path ./locales
160+
161+
# Auto-translate Android app
162+
lrm translate --provider google --to es,fr --path ./app/src/main/res
156163
```
157164

165+
### Android Support
166+
- **Folder structure:** `res/values/strings.xml` (default), `res/values-es/strings.xml` (Spanish)
167+
- **Full support for:** `<string>`, `<plurals>`, `<string-array>`
168+
- **Comments and `translatable="false"` preserved**
169+
170+
### iOS Support
171+
- **Folder structure:** `en.lproj/Localizable.strings`, `es.lproj/Localizable.strings`
172+
- **Full support for:** `.strings` and `.stringsdict` (plurals)
173+
- **Comments preserved in standard iOS format**
174+
158175
**👉 [See JSON Localization Guide →](docs/JSON.md)** | [.NET NuGet Package →](LocalizationManager.JsonLocalization/README.md)
159176

160177
---
@@ -171,11 +188,14 @@ lrm convert --from resx --to json --path ./Resources
171188
- Secure API key management (environment variables, encrypted store, or config file)
172189
- Customizable models, prompts, and endpoints for AI providers
173190
- Plural form translation with CLDR support (zero/one/two/few/many/other)
174-
- **📦 JSON Localization** - Full support for JSON resource files alongside .resx
175-
- Standard JSON format with nested keys and `_plural` markers
176-
- i18next compatibility mode with suffix-based plurals (`_one`, `_other`)
177-
- Auto-detection of format from file naming patterns
178-
- Comments and metadata preservation
191+
- **📦 Multi-Format Support** - Unified management for all major localization formats
192+
- **.resx** - .NET resource files with full XML preservation
193+
- **JSON** - Standard JSON with nested keys and `_plural` markers
194+
- **i18next** - Full compatibility with suffix-based plurals (`_one`, `_other`)
195+
- **Android** - `strings.xml` with `<plurals>` and `<string-array>` support
196+
- **iOS** - `.strings` and `.stringsdict` with plural support
197+
- Auto-detection of format from file patterns
198+
- Comments and metadata preservation across all formats
179199
- [See JSON format usage guide →](LocalizationManager.JsonLocalization/README.md)
180200
- **🚀 CI/CD Automation** - Production-ready workflows for GitHub Actions, GitLab CI, Azure DevOps
181201
- Validate → Check Missing → Auto-Translate → Re-validate → Commit

cloud/deploy/.lrm/cloud.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI1NWEyNjlhMS1jZmYzLTQ0ZGQtYTU4NC01ZjIzZGRlOGM4NjMiLCJuYW1laWQiOiIxIiwiZW1haWwiOiJ0ZXN0dXNlckBleGFtcGxlLmNvbSIsInVuaXF1ZV9uYW1lIjoidGVzdHVzZXIiLCJhdXRoX3R5cGUiOiJlbWFpbCIsInBsYW4iOiJ0ZWFtIiwiZW1haWxfdmVyaWZpZWQiOiJUcnVlIiwiaXNfc3VwZXJhZG1pbiI6InRydWUiLCJuYmYiOjE3NjYyNDM5NjIsImV4cCI6MTc2NjMzMDM2MiwiaWF0IjoxNzY2MjQzOTYyLCJpc3MiOiJscm1jbG91ZCIsImF1ZCI6ImxybWNsb3VkIn0.108f49w4gYMbjsxk2RGb9y36DRaOXRfmuzLrTj6jCNk",
3+
"apiKey": "lrm_zudLTqONERzutxImdtPDxkQNFMo8",
4+
"updatedAt": "2025-12-20T18:55:26.3107619Z"
5+
}

cloud/src/www/docs/index.html

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -658,12 +658,14 @@ <h1>Documentation</h1>
658658
<div class="container">
659659
<div class="ecosystem-intro">
660660
<h2>The LRM Ecosystem</h2>
661-
<p>LRM is a complete localization toolkit that works with any platform. Whether you're building with .NET, React, Vue, Angular, or Node.js, LRM provides the tools you need to manage, validate, translate, and collaborate on your localization resources.</p>
661+
<p>LRM is a complete localization toolkit that works with any platform. Whether you're building with .NET, React, Vue, Angular, Node.js, Android, or iOS, LRM provides the tools you need to manage, validate, translate, and collaborate on your localization resources.</p>
662662
<p>All LRM tools work with the same resource files, so you can use whichever interface fits your workflow - automate with the CLI, edit interactively in the terminal, get real-time feedback in VS Code, or collaborate with your team in the cloud.</p>
663663
<div class="formats">
664664
<span class="format-badge">.resx</span>
665665
<span class="format-badge">JSON</span>
666666
<span class="format-badge">i18next</span>
667+
<span class="format-badge">Android</span>
668+
<span class="format-badge">iOS</span>
667669
</div>
668670
</div>
669671

@@ -672,7 +674,7 @@ <h2>The LRM Ecosystem</h2>
672674
<div class="diagram-layer">
673675
<div class="diagram-files">
674676
<h3>Your Localization Files</h3>
675-
<p>.resx &middot; JSON &middot; i18next</p>
677+
<p>.resx &middot; JSON &middot; i18next &middot; Android &middot; iOS</p>
676678
</div>
677679
<div class="diagram-connector"></div>
678680
</div>
@@ -708,17 +710,19 @@ <h4>Cloud</h4>
708710
<span class="platform-badge">Vue</span>
709711
<span class="platform-badge">Angular</span>
710712
<span class="platform-badge">Node.js</span>
711-
<span class="platform-badge">Any JSON i18n</span>
713+
<span class="platform-badge">Android</span>
714+
<span class="platform-badge">iOS</span>
712715
</div>
713716

714717
<!-- Layer 3: Target Platforms -->
715718
<div class="diagram-branch">
716719
<div class="diagram-target">
717-
<h4>Web Applications</h4>
718-
<p>React, Vue, Angular, Node.js</p>
720+
<h4>Web & Mobile Applications</h4>
721+
<p>React, Vue, Angular, Node.js, Android, iOS</p>
719722
<ul>
720-
<li>Use i18next or JSON natively</li>
721-
<li>Manage resources with LRM tools</li>
723+
<li>Android: strings.xml with plurals support</li>
724+
<li>iOS: .strings and .stringsdict formats</li>
725+
<li>i18next or JSON for web apps</li>
722726
<li>Translate with 10+ providers</li>
723727
</ul>
724728
</div>

docs/CONFIGURATION.md

Lines changed: 96 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ YourProject/
6767
```json
6868
{
6969
"DefaultLanguageCode": "en",
70-
"ResourceFormat": "resx",
70+
"ResourceFormat": "resx", // or "json", "i18next", "android", "ios"
7171
"Json": {
7272
"UseNestedKeys": true,
7373
"IncludeMeta": true,
@@ -148,23 +148,33 @@ YourProject/
148148

149149
**Type:** `string`
150150
**Default:** Auto-detect based on files in resource path
151-
**Values:** `"resx"` or `"json"`
151+
**Values:** `"resx"`, `"json"`, `"i18next"`, `"android"`, or `"ios"`
152152

153153
```json
154154
{
155-
"ResourceFormat": "json"
155+
"ResourceFormat": "android"
156156
}
157157
```
158158

159159
**Behavior:**
160160
- Specifies which resource file backend to use
161-
- If not set, auto-detects based on existing files (`.resx` files → RESX backend, `.json` files → JSON backend)
161+
- If not set, auto-detects based on existing files and folder structure
162162
- When set explicitly, forces the specified backend regardless of detected files
163163

164+
**Auto-detection patterns:**
165+
| Format | Detection Pattern |
166+
|--------|-------------------|
167+
| `resx` | `*.resx` files |
168+
| `json` | `strings*.json` or `*.{culture}.json` files |
169+
| `i18next` | `{culture}.json` files (e.g., `en.json`, `fr.json`) |
170+
| `android` | `res/values*/strings.xml` folder structure |
171+
| `ios` | `*.lproj/*.strings` folder structure |
172+
164173
**Use Cases:**
165-
- Force JSON format when both `.resx` and `.json` files exist
166-
- Initialize a new project with JSON format before creating resource files
174+
- Force a specific format when multiple formats exist
175+
- Initialize a new project before creating resource files
167176
- Override auto-detection when switching formats
177+
- Specify Android or iOS for mobile projects
168178

169179
---
170180

@@ -233,6 +243,86 @@ Produces: `en.json`, `fr.json`, `de.json`
233243

234244
---
235245

246+
### Android
247+
248+
**Type:** `object`
249+
**Purpose:** Configure Android resource file format (only applies when `ResourceFormat` is `"android"`)
250+
251+
```json
252+
{
253+
"ResourceFormat": "android",
254+
"Android": {
255+
"BaseName": "strings"
256+
}
257+
}
258+
```
259+
260+
#### Android Options
261+
262+
| Option | Type | Default | Description |
263+
|--------|------|---------|-------------|
264+
| `BaseName` | string | `"strings"` | Base filename for resources (produces `strings.xml`) |
265+
266+
**Folder Structure:**
267+
```
268+
res/
269+
├── values/ # Default language
270+
│ └── strings.xml
271+
├── values-es/ # Spanish
272+
│ └── strings.xml
273+
├── values-fr/ # French
274+
│ └── strings.xml
275+
└── values-zh-rCN/ # Chinese (Simplified)
276+
└── strings.xml
277+
```
278+
279+
**Supported Elements:**
280+
- `<string name="key">value</string>` - Simple strings
281+
- `<plurals name="key">` - Plural forms with CLDR quantities
282+
- `<string-array name="key">` - String arrays
283+
- `translatable="false"` attribute preserved
284+
285+
---
286+
287+
### Ios
288+
289+
**Type:** `object`
290+
**Purpose:** Configure iOS resource file format (only applies when `ResourceFormat` is `"ios"`)
291+
292+
```json
293+
{
294+
"ResourceFormat": "ios",
295+
"Ios": {
296+
"BaseName": "Localizable"
297+
}
298+
}
299+
```
300+
301+
#### iOS Options
302+
303+
| Option | Type | Default | Description |
304+
|--------|------|---------|-------------|
305+
| `BaseName` | string | `"Localizable"` | Base filename for resources (produces `Localizable.strings`) |
306+
307+
**Folder Structure:**
308+
```
309+
en.lproj/
310+
├── Localizable.strings # Simple strings
311+
└── Localizable.stringsdict # Plurals (optional)
312+
es.lproj/
313+
├── Localizable.strings
314+
└── Localizable.stringsdict
315+
fr.lproj/
316+
├── Localizable.strings
317+
└── Localizable.stringsdict
318+
```
319+
320+
**Supported Formats:**
321+
- `.strings` - Simple key-value pairs with comments
322+
- `.stringsdict` - Plist format for plural forms
323+
324+
---
325+
236326
### Translation
237327

238328
**Type:** `object`

0 commit comments

Comments
 (0)