Skip to content

Repository files navigation

Xloca Core

English | 简体中文

Xloca Core is a pure Rust, cross-platform toolkit for Apple localization formats. It provides the xloca command-line tool and the xloca_core Rust library for reading, validating, converting, merging, exporting, inspecting, and editing String Catalog resources.

Contents

Features

  • Strict .xcstrings parsing from files or byte slices.
  • Duplicate JSON property detection before a document is built.
  • Preservation of entry order, unknown JSON fields, and future schema data.
  • Typed recursive access to stringUnit, variations, and substitutions.
  • XML and binary plist .stringsdict support on macOS, Linux, and Windows.
  • Conversion of plural, device, width, nested, and positional substitution rules into multi-locale String Catalogs.
  • CI lint output in text, JSON, SARIF, and GitHub Actions formats.
  • Coverage reports in text, JSON, CSV, and Markdown.
  • Catalog merging with configurable conflict and missing-key strategies.
  • .strings and .stringsdict export in text, XML, or binary form.
  • Catalog editing, diffing, pseudo-localization, and project scanning.
  • Atomic file replacement for generated Catalog and resource files.

Installation

From crates.io

For a published release, install the CLI directly from crates.io:

cargo install xloca-core

The package name is xloca-core; the installed executable is named xloca.

xloca --version
xloca --help

To use the library in another Rust project:

cargo add xloca-core

Then import it with the Rust crate name xloca_core:

use xloca_core::CatalogParser;

From source

Requirements:

  • Rust 1.88 or newer.
  • Cargo from the same Rust toolchain.

Build and install locally:

cargo build --release
cargo install --path .

The release binary is also available at target/release/xloca on Unix-like systems and target/release/xloca.exe on Windows.

Quick start

Inspect a Catalog:

xloca inspect Localizable.xcstrings

Convert localized .stringsdict resources:

xloca convert-stringsdict Resources \
  --table Localizable \
  --source-language en \
  --output Localizable.xcstrings

Lint it in CI:

xloca lint Localizable.xcstrings --format sarif --deny-warnings

Export legacy resources:

xloca export Localizable.xcstrings \
  --output Resources \
  --table Localizable

Input conventions and safety

Supported formats

  • String Catalog: .xcstrings
  • Legacy strings table: .strings
  • Legacy variation dictionary: .stringsdict

Catalogs and text .strings files must be UTF-8. .stringsdict input may be an XML or binary property list.

Locale inference

For legacy files, the locale is inferred from an ancestor directory named <locale>.lproj, for example en.lproj, fr.lproj, or zh-Hans.lproj.

  • --locale overrides inference for all applicable inputs.
  • Base.lproj does not produce a Base locale; the source language is used as the fallback where supported.
  • Locale matching inside a Catalog is case-insensitive.

Table inference

The table name is the input file stem: Localizable.strings belongs to the Localizable table and Errors.stringsdict belongs to Errors.

  • Directory conversion or merge fails when it discovers multiple tables unless --table selects one.
  • Explicitly listed merge files may have different names because that choice is considered intentional.

File safety

  • Modified Catalogs are fully validated before replacement.
  • Generated files use temporary files in the destination directory and are atomically persisted.
  • Merge and scan generation exclude their own output file, so an output located inside an input tree is safe to regenerate.
  • Export locale and table names must be safe single path components.
  • Export synchronizes the selected table and resource kinds and removes stale files for locales that no longer contain matching entries.

Command overview

Command Purpose
inspect Parse, validate, and summarize a String Catalog.
convert-stringsdict Convert one or more .stringsdict inputs into a Catalog.
lint Run structural, translation, placeholder, and Xcode compatibility checks.
merge Merge Catalogs and legacy localization resources.
export Export a Catalog to .strings and .stringsdict.
stats Report translation coverage and state totals.
set-state Set stringUnit.state recursively for one locale.
rename-key Rename a Catalog key.
remove-locale Remove a locale from every entry.
set-comment Set or replace an entry comment.
diff Compare Catalog content, states, placeholders, and root metadata.
pseudo Generate an accented or RTL pseudo-localized locale.
scan Discover resources, conflicts, project locales, and generate Catalogs.

Global options:

Option Description
-h, --help Show general or subcommand help.
-V, --version Show the installed version.

Command reference

inspect

xloca inspect [OPTIONS] <CATALOG>
Argument or option Required/default Description
<CATALOG> Required .xcstrings file to parse and validate.
--format <FORMAT> text text for humans or json for programmatic callers.

The summary includes the canonical file path, source language, schema version, string count, discovered locales, and whether the Catalog is editable. The command fails when parsing or structural validation reports an error. JSON output also includes has_document and structured diagnostics so callers can distinguish an unreadable Catalog from one that can be opened read-only.

convert-stringsdict

xloca convert-stringsdict [OPTIONS] --output <OUTPUT> \
  --source-language <SOURCE_LANGUAGE> <INPUTS>...
Argument or option Required/default Description
<INPUTS>... Required .stringsdict files or directories scanned recursively.
-o, --output <OUTPUT> Required Destination .xcstrings file.
-s, --source-language <LANG> Required Catalog development/source language.
-l, --locale <LOCALE> Inferred Override the locale for every input file.
--table <TABLE> Auto Select one table from directory inputs.
--catalog-version <VERSION> 1.0 Schema version written to the generated Catalog.

Conversion rejects malformed inputs, duplicate plist keys, unsupported rules, unreferenced rules, and ambiguous structures instead of silently dropping data. The generated Catalog is validated before the output is replaced.

Examples:

xloca convert-stringsdict en.lproj/Localizable.stringsdict \
  --source-language en \
  --output Localizable.xcstrings

xloca convert-stringsdict Resources \
  --table Localizable \
  --source-language en \
  --output Localizable.xcstrings

lint

xloca lint [OPTIONS] <CATALOG>
Argument or option Required/default Description
<CATALOG> Required Catalog to check.
--format <FORMAT> text text, json, sarif, or github.
--fix Off Apply deterministic safe fixes before reporting.
--source-language <LANG> None Add a source language when missing; used with --fix.
--sort Off Sort Catalog keys; requires --fix.
-l, --locale <LOCALE> Catalog locales Require a locale even when it is absent from all entries. Repeatable.
--deny-warnings Off Return a failure status when warnings are present.

Checks include missing and empty translations, review/stale states, invalid states, printf signature mismatches, substitutions, locale spelling collisions, plural categories, variation ordering, and unsupported Xcode structures.

Safe fixes can normalize locale spelling, add required fields, normalize known states, remove empty localization objects, and sort keys. Fixes are validated before the Catalog is written.

xloca lint Localizable.xcstrings
xloca lint Localizable.xcstrings --format github --deny-warnings
xloca lint Localizable.xcstrings --fix --source-language en --sort

merge

xloca merge [OPTIONS] --output <OUTPUT> <INPUTS>...
Argument or option Required/default Description
<INPUTS>... Required Catalog, .strings, .stringsdict, or directory inputs.
-o, --output <OUTPUT> Required Destination String Catalog.
-s, --source-language <LANG> Inferred/en fallback Output source language and fallback locale for legacy files.
-l, --locale <LOCALE> Inferred Override locale inference for legacy inputs.
--table <TABLE> Auto Select one table from directory inputs.
--conflict <MODE> keep-existing error, keep-existing, overwrite, or prefer-translated.
--missing <MODE> keep keep, remove, or mark-stale for keys only in the first input.

Conflict modes:

Mode Behavior
error Stop at the first unequal value for the same key/locale/field.
keep-existing Keep the value already present in the merge base.
overwrite Replace existing values with incoming values.
prefer-translated Replace only when incoming localization content is complete and translated while existing content is not.

The first expanded input is the merge base. Directory entries are sorted for deterministic results. The output path is excluded from recursive discovery.

xloca merge Base.xcstrings Feature.xcstrings fr.lproj/Localizable.strings \
  --source-language en \
  --conflict prefer-translated \
  --output Localizable.xcstrings

xloca merge Resources \
  --table Localizable \
  --source-language en \
  --output Resources/Localizable.xcstrings

export

xloca export [OPTIONS] --output <OUTPUT> <CATALOG>
Argument or option Required/default Description
<CATALOG> Required Source String Catalog.
-o, --output <OUTPUT> Required Directory that receives <locale>.lproj folders.
--table <TABLE> Catalog file stem Output filename without an extension.
-l, --locale <LOCALE> All Catalog locales Export only the selected locale. Repeatable.
--kind <KIND> all strings, stringsdict, or all.
--serialization <MODE> text text, xml, or binary.

Serialization behavior:

Mode .strings .stringsdict
text OpenStep strings syntax XML plist
xml XML plist dictionary XML plist
binary Binary plist dictionary Binary plist

Direct strings are written to .strings; plural, device, width, and substitution resources are written to .stringsdict. When applicable, a fallback direct string is also emitted. Unsupported legacy shapes cause the command to fail before planned files are written.

xloca export Localizable.xcstrings \
  --output Resources \
  --table Localizable \
  --kind all \
  --serialization text

stats

xloca stats [OPTIONS] <CATALOG>
Argument or option Required/default Description
<CATALOG> Required Catalog to summarize.
--format <FORMAT> text text, json, csv, or markdown.
-l, --locale <LOCALE> All locales Include only selected locales. Repeatable.
--minimum <PERCENT> None Fail if any selected locale is below 0 through 100.

The report includes total, present, completed, missing, pending, review, stale, direct, variation, plural, device, width, substitution, and coverage values. Implicit source-language keys count as complete source content.

xloca stats Localizable.xcstrings --format markdown --minimum 95

Editing commands

All editing commands validate the complete Catalog before atomically replacing the input file.

set-state

xloca set-state --locale <LOCALE> --state <STATE> [--key <KEY>] <CATALOG>
Argument or option Required/default Description
<CATALOG> Required Catalog to modify.
--locale <LOCALE> Required Locale whose nested stringUnit values are updated.
--state <STATE> Required New non-empty state. Common values: translated, needs_review, new, stale.
--key <KEY> All keys Restrict the change to one Catalog key.

rename-key

xloca rename-key <CATALOG> <OLD_KEY> <NEW_KEY>

The command preserves entry order and content and fails if <NEW_KEY> already exists.

remove-locale

xloca remove-locale <CATALOG> <LOCALE>

Locale matching is case-insensitive. Every matching localization is removed.

set-comment

xloca set-comment <CATALOG> <KEY> <COMMENT>

The key must already exist. Quote comments containing whitespace in the shell.

diff

xloca diff [OPTIONS] <OLD> <NEW>
Argument or option Required/default Description
<OLD> Required Baseline Catalog.
<NEW> Required Catalog to compare.
--format <FORMAT> text text, json, or markdown.
--check Off Return a failure status when any difference exists.

Diff output covers added, removed, and modified keys; locale changes; entry fields; translation values; states; placeholder-sensitive changes; and root metadata such as sourceLanguage, version, and unknown future fields.

xloca diff Old.xcstrings New.xcstrings --format json --check

pseudo

xloca pseudo [OPTIONS] --locale <LOCALE> <CATALOG>
Argument or option Required/default Description
<CATALOG> Required Source Catalog.
-l, --locale <LOCALE> Required Target pseudo locale, commonly en-XA or ar-XB.
-o, --output <OUTPUT> Input Catalog Destination Catalog; omission edits in place.
--source-locale <LOCALE> Catalog source language Locale to clone before transformation.
--overwrite Off Replace existing target localizations.
--rtl Off Wrap output with RTL override markers. Automatically enabled for ar-XB.
--expansion <PERCENT> 30 Append expansion padding; accepted range is 0 through 300.

Printf placeholders, escaped percent tokens, and Catalog substitution markers are preserved while visible text is accented and expanded.

xloca pseudo Localizable.xcstrings \
  --locale en-XA \
  --output Pseudo.xcstrings

xloca pseudo Localizable.xcstrings --locale ar-XB --rtl

scan

xloca scan [OPTIONS] <PATHS>...
Argument or option Required/default Description
<PATHS>... Required Project files or resource trees to scan recursively.
--format <FORMAT> text text or json.
--catalog-output <DIR> None Generate one merged .xcstrings file per discovered table.
--source-language <LANG> Required with output Source language for generated Catalogs.
--fail-on-conflicts Off Return a failure status when duplicate resource groups are found.

The scanner discovers .xcstrings, .strings, and .stringsdict resources; groups them by kind, table, and locale; reports duplicates; reads Xcode knownRegions; reads Swift package defaultLocalization; and reports declared locales without resources.

Generated output files are excluded on subsequent runs, even when the output directory is inside a scanned tree.

xloca scan . --format json --fail-on-conflicts

xloca scan . \
  --catalog-output Generated \
  --source-language en

Exit status

Code Meaning
0 Command completed successfully.
2 Invalid arguments, parsing/validation failure, I/O failure, lint failure, diff --check difference, coverage threshold failure, or requested conflict failure.

Machine-readable reports are written to standard output. Diagnostics and final error summaries are written to standard error.

Rust library

Add the dependency:

cargo add xloca-core

Parse a Catalog:

use xloca_core::CatalogParser;

fn main() {
    let result = CatalogParser::default().parse_path("Localizable.xcstrings");

    for diagnostic in &result.diagnostics {
        eprintln!("{diagnostic}");
    }

    if let Some(document) = result.document {
        println!("{} strings", document.strings.len());
        println!("locales: {}", document.locales.join(", "));
    }
}

Main library entry points:

  • CatalogParser::parse_path / CatalogParser::parse_bytes
  • StringsdictConverter::convert
  • lint_catalog / fix_catalog
  • merge_catalogs / merge_catalogs_filtered
  • export_catalog
  • catalog_stats / diff_catalogs
  • generate_pseudo
  • scan_project / generate_catalogs_from_scan
  • write_json_atomic

Diagnostics include a stable code, severity, source, JSON/plist pointer, message, and optional line/column information.

Development

Run the complete local verification suite:

cargo test --all-targets
cargo test --doc
cargo clippy --all-targets --all-features -- -D warnings
cargo fmt --all -- --check

Build optimized artifacts:

cargo build --release

License

MIT

About

Cross-platform Rust toolkit for Apple’s localization formats.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages