diff --git a/.github/workflows/l10n.yml b/.github/workflows/l10n.yml index c8ba5f74faf..0254b6e98b5 100644 --- a/.github/workflows/l10n.yml +++ b/.github/workflows/l10n.yml @@ -1118,6 +1118,29 @@ jobs: env: RUST_BACKTRACE: "1" + l10n_embedded_locale_regression_test: + name: L10n/Embedded Locale Regression Test + runs-on: ubuntu-latest + env: + SCCACHE_GHA_ENABLED: "true" + RUSTC_WRAPPER: "sccache" + steps: + - uses: actions/checkout@v5 + with: + persist-credentials: false + - uses: dtolnay/rust-toolchain@stable + - uses: Swatinem/rust-cache@v2 + - name: Run sccache-cache + uses: mozilla-actions/sccache-action@v0.0.9 + - name: Install/setup prerequisites + shell: bash + run: | + sudo apt-get -y update && sudo apt-get -y install libselinux1-dev + - name: Test embedded locale functionality for individual utilities + shell: bash + run: | + bash util/test_locale_regression.sh + l10n_locale_embedding_regression_test: name: L10n/Locale Embedding Regression Test runs-on: ubuntu-latest diff --git a/src/uucore/build.rs b/src/uucore/build.rs index 1fa88d93c04..29ffb2d86dc 100644 --- a/src/uucore/build.rs +++ b/src/uucore/build.rs @@ -12,7 +12,6 @@ pub fn main() -> Result<(), Box> { let out_dir = env::var("OUT_DIR")?; let mut embedded_file = File::create(Path::new(&out_dir).join("embedded_locales.rs"))?; - writeln!(embedded_file, "// Generated at compile time - do not edit")?; writeln!( embedded_file, @@ -78,6 +77,14 @@ fn detect_target_utility() -> Option { } } + // Auto-detect utility name from CARGO_PKG_NAME if it's a uu_* package + if let Ok(pkg_name) = env::var("CARGO_PKG_NAME") { + if let Some(util_name) = pkg_name.strip_prefix("uu_") { + println!("cargo:warning=Auto-detected utility name: {}", util_name); + return Some(util_name.to_string()); + } + } + // Check for a build configuration file in the target directory if let Ok(target_dir) = env::var("CARGO_TARGET_DIR") { let config_path = std::path::Path::new(&target_dir).join("uucore_target_util.txt"); @@ -156,6 +163,9 @@ fn embed_all_utilities_locales( // Discover all uu_* directories let src_uu_dir = project_root.join("src/uu"); if !src_uu_dir.exists() { + // When src/uu doesn't exist (e.g., standalone uucore from crates.io), + // embed a static list of utility locales that are commonly used + embed_static_utility_locales(embedded_file)?; return Ok(()); } @@ -202,3 +212,60 @@ fn embed_all_utilities_locales( embedded_file.flush()?; Ok(()) } + +fn embed_static_utility_locales( + embedded_file: &mut std::fs::File, +) -> Result<(), Box> { + use std::env; + + writeln!( + embedded_file, + " // Static utility locales for crates.io builds" + )?; + + let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap_or_default(); + let Some(registry_dir) = Path::new(&manifest_dir).parent() else { + return Ok(()); // nothing to scan + }; + + // First, try to embed uucore locales - critical for common translations like "Usage:" + let uucore_locale_file = Path::new(&manifest_dir).join("locales/en-US.ftl"); + if uucore_locale_file.is_file() { + let content = std::fs::read_to_string(&uucore_locale_file)?; + writeln!(embedded_file, " // Common uucore locale")?; + writeln!( + embedded_file, + " locales.insert(\"uucore/en-US.ftl\", r###\"{content}\"###);" + )?; + writeln!(embedded_file)?; + } + + // Collect and sort for deterministic builds + let mut entries: Vec<_> = std::fs::read_dir(registry_dir)? + .filter_map(Result::ok) + .collect(); + entries.sort_by_key(|e| e.file_name()); + + for entry in entries { + let file_name = entry.file_name(); + if let Some(dir_name) = file_name.to_str() { + // Match uu_- + if let Some((util_part, _)) = dir_name.split_once('-') { + if let Some(util_name) = util_part.strip_prefix("uu_") { + let locale_file = entry.path().join("locales/en-US.ftl"); + if locale_file.is_file() { + let content = std::fs::read_to_string(&locale_file)?; + writeln!(embedded_file, " // Locale for {util_name}")?; + writeln!( + embedded_file, + " locales.insert(\"{util_name}/en-US.ftl\", r###\"{content}\"###);" + )?; + writeln!(embedded_file)?; + } + } + } + } + } + + Ok(()) +} diff --git a/util/test_locale_regression.sh b/util/test_locale_regression.sh new file mode 100755 index 00000000000..090a3eef05b --- /dev/null +++ b/util/test_locale_regression.sh @@ -0,0 +1,34 @@ +#!/bin/bash + +echo "Testing embedded locale functionality to prevent crates.io regression..." +INSTALL_DIR="$(pwd)/test-install-dir" +rm -rf "$INSTALL_DIR" +mkdir -p "$INSTALL_DIR" +utilities_to_test=("cp" "mv" "ln") + +for util in "${utilities_to_test[@]}"; do + echo "Testing $util..." + cargo install --path "src/uu/$util" --root "$INSTALL_DIR" --force --quiet + binary_path="$INSTALL_DIR/bin/$util" + help_output=$("$binary_path" --help 2>&1) + + # Check for regression indicators + if echo "$help_output" | grep -q "common-usage"; then + echo "✗ CRITICAL REGRESSION: $util shows untranslated 'common-usage' key" + echo "Help output:" + echo "$help_output" + exit 1 + fi + + # Verify proper "Usage:" label + if echo "$help_output" | grep -q "Usage:"; then + echo "✓ $util shows properly translated 'Usage:' label" + else + echo "✗ CRITICAL REGRESSION: $util missing 'Usage:' label" + echo "Help output:" + echo "$help_output" + exit 1 + fi +done + +echo "All tests passed!"