Skip to content

Commit dd264a3

Browse files
committed
test(cli): add normalize format coverage and documentation
1 parent deadbbc commit dd264a3

4 files changed

Lines changed: 121 additions & 2 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,12 @@ The format is based on Keep a Changelog and this project follows Semantic Versio
1010
- Global `--strict` mode in CLI workflows and strict-read controls in library input options.
1111
- CLI `diff` command with human-readable and JSON report output.
1212
- Plural validation enhancements and richer stats reporting.
13+
- CLI `normalize` command support across Apple `.strings`, `.xcstrings`, Android `strings.xml`, CSV, and TSV formats, including drift detection with `--check`.
1314

1415
### Changed
1516
- Ongoing improvements to conversion coverage across Apple, Android, CSV, and TSV formats.
1617
- Documentation and contributor guidance updates.
18+
- Normalize workflow documentation, including `--dry-run`, `--no-placeholders`, `--key-style`, multi-input `--output` constraints, and `--continue-on-error` behavior.
1719

1820
### Fixed
1921
- Multiple parser/writer correctness and conversion edge-case fixes covered by expanded tests.

README.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
Universal localization toolkit: library + CLI for Apple/Android/CSV/TSV.
44

55
- Library crate (`langcodec`): parse, write, convert, merge with a unified model
6-
- CLI crate (`langcodec-cli`): convert, diff, merge, sync, view, stats, debug, edit
6+
- CLI crate (`langcodec-cli`): convert, diff, merge, sync, view, stats, debug, edit, normalize
77

88
---
99

@@ -59,6 +59,7 @@ This is a `0.7.0` release available on [crates.io](https://crates.io/crates/lang
5959
- Convert: `langcodec convert -i input.strings -o strings.xml`
6060
- Diff: `langcodec diff --source A.xcstrings --target B.xcstrings --json`
6161
- Edit (add/update/remove): `langcodec edit set -i 'locales/**/*.strings' -k welcome -v "Hello"` (use `--dry-run` to preview)
62+
- Normalize and detect drift: `langcodec normalize -i 'locales/**/*.{strings,xml,csv,tsv,xcstrings}' --check`
6263
- Sync existing keys only: `langcodec sync --source A.xcstrings --target B.xcstrings --match-lang en`
6364
- View: `langcodec view -i strings.xml --full`
6465
- Stats (JSON): `langcodec stats -i Localizable.xcstrings --json`
@@ -92,6 +93,10 @@ This is a `0.7.0` release available on [crates.io](https://crates.io/crates/lang
9293
- The convert command also supports custom JSON/YAML input formats.
9394
- The CLI will error if you try to merge files of different formats.
9495
- Edit supports multiple inputs and glob patterns. When multiple inputs are provided, edits are applied in-place and `--output` is not allowed.
96+
- Normalize supports `.strings`, Android `strings.xml`, `.csv`, `.tsv`, and `.xcstrings`.
97+
- Normalize options: `--check` (fail on drift), `--dry-run` (preview only), `--no-placeholders` (skip placeholder canonicalization), `--key-style` (`none|snake|kebab|camel`).
98+
- Normalize multi-input constraint: `--output` is single-input only; pair multi-input workflows with in-place writes.
99+
- Normalize `--continue-on-error` processes all inputs and returns non-zero if any file fails.
95100
- Android path inference: `values/strings.xml` (no qualifier) defaults to English (`en`).
96101
- When converting to `.xcstrings`, if `source_language` or `version` metadata is missing, the CLI defaults them to `en` and `1.0` respectively (overridable via flags).
97102

langcodec-cli/README.md

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
Universal CLI for converting, inspecting, merging, and editing localization files.
44

55
- Formats: Apple `.strings`, `.xcstrings`, Android `strings.xml`, CSV, TSV
6-
- Commands: convert, diff, merge, sync, view, stats, debug, edit
6+
- Commands: convert, diff, merge, sync, view, stats, debug, edit, normalize
77

88
## Install
99

@@ -121,6 +121,39 @@ Options:
121121

122122
Supported formats: .strings, .xml (Android), .xcstrings, .csv, .tsv. Custom JSON/YAML/.langcodec edit is currently not enabled.
123123

124+
### normalize
125+
126+
Normalize localization files in-place (or to `--output` in single-input mode).
127+
128+
```sh
129+
# Normalize in-place
130+
langcodec normalize -i en.lproj/Localizable.strings
131+
132+
# CI drift check (non-zero if any file would change)
133+
langcodec normalize -i 'locales/**/*.{strings,xml,csv,tsv,xcstrings}' --check
134+
135+
# Preview without writing
136+
langcodec normalize -i values/strings.xml --dry-run
137+
138+
# Disable placeholder normalization and rename keys to snake_case
139+
langcodec normalize -i Localizable.xcstrings --no-placeholders --key-style snake
140+
141+
# Keep processing remaining files and summarize failures at the end
142+
langcodec normalize -i a.strings -i b.csv -i c.tsv --continue-on-error
143+
```
144+
145+
Options and behavior:
146+
147+
- --check: Detects normalization drift and exits non-zero when a file would change.
148+
- --dry-run: Prints what would change and exits without writing files.
149+
- --no-placeholders: Skips placeholder canonicalization (for example `%@``%s`).
150+
- --key-style: Renames keys during normalization. Values: `none` (default), `snake`, `kebab`, `camel`.
151+
- --output/-o: Single-input mode only. If multiple inputs are provided, `--output` is rejected.
152+
- --continue-on-error: Continues processing all matched inputs, prints a summary, and exits non-zero if any file failed.
153+
- --inputs/-i: One or more files, including quoted glob patterns.
154+
155+
Supported normalize formats: `.strings`, Android `strings.xml`, `.csv`, `.tsv`, `.xcstrings`.
156+
124157
## Notes
125158

126159
- Android plurals `<plurals>` are supported.

langcodec-cli/tests/normalize_cli_tests.rs

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,85 @@ fn test_normalize_dry_run_does_not_write() {
8888
assert_eq!(after, before);
8989
}
9090

91+
#[test]
92+
fn test_normalize_check_detects_drift_across_standard_formats() {
93+
let temp_dir = TempDir::new().unwrap();
94+
let cases = [
95+
(
96+
"apple_strings",
97+
"en.strings",
98+
"\"z\" = \"%@\";\n\"a\" = \"A\";\n",
99+
),
100+
(
101+
"android_xml",
102+
"strings.xml",
103+
r#"<?xml version="1.0" encoding="utf-8"?>
104+
<resources>
105+
<string name="z">Hello %@</string>
106+
<string name="a">A</string>
107+
</resources>
108+
"#,
109+
),
110+
("csv", "translations.csv", "z,%@\na,A\n"),
111+
("tsv", "translations.tsv", "z\t%@\na\tA\n"),
112+
(
113+
"xcstrings",
114+
"Localizable.xcstrings",
115+
r#"{
116+
"sourceLanguage": "en",
117+
"version": "1.0",
118+
"strings": {
119+
"z": {
120+
"localizations": {
121+
"en": {
122+
"stringUnit": {
123+
"state": "translated",
124+
"value": "Hello %@"
125+
}
126+
}
127+
}
128+
},
129+
"a": {
130+
"localizations": {
131+
"en": {
132+
"stringUnit": {
133+
"state": "translated",
134+
"value": "A"
135+
}
136+
}
137+
}
138+
}
139+
}
140+
}
141+
"#,
142+
),
143+
];
144+
145+
for (label, filename, contents) in cases {
146+
let input = temp_dir.path().join(filename);
147+
fs::write(&input, contents).unwrap();
148+
149+
let output = langcodec_cmd()
150+
.args(["normalize", "-i", input.to_str().unwrap(), "--check"])
151+
.output()
152+
.unwrap();
153+
154+
assert!(
155+
!output.status.success(),
156+
"{label} should report drift in --check mode"
157+
);
158+
let combined = format!(
159+
"{}{}",
160+
String::from_utf8_lossy(&output.stdout),
161+
String::from_utf8_lossy(&output.stderr)
162+
);
163+
assert!(
164+
combined.contains("would change"),
165+
"{label} expected drift message, got: {combined}"
166+
);
167+
}
168+
}
169+
91170
#[test]
92171
fn test_normalize_output_written_even_when_unchanged() {
93172
let temp_dir = TempDir::new().unwrap();

0 commit comments

Comments
 (0)