Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,15 @@ jobs:
- uses: actions/checkout@v6
- name: 'Test ICU version ${{ matrix.icu_version }}'
run: 'make DOCKER_TEST_ENV=rust_icu_testenv-${{ matrix.icu_version}} docker-test'
test-examples:
runs-on: ubuntu-latest
strategy:
matrix:
icu_version: [74, 76, 77]
steps:
- uses: actions/checkout@v6
- name: 'Build and run examples on ICU version ${{ matrix.icu_version }}'
run: 'make DOCKER_TEST_ENV=rust_icu_testenv-${{ matrix.icu_version }} RUST_ICU_MAJOR_VERSION_NUMBER=${{ matrix.icu_version }} docker-test-example'
test-with-features:
runs-on: ubuntu-latest
strategy:
Expand Down
7 changes: 7 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,11 @@ members = [
"rust_icu_utext",
"rust_icu_utrans",
]
# Examples are standalone crates that build against the workspace crates via
# `path` dependencies. They are excluded from the workspace so the feature
# matrix used in CI (`cargo test --no-default-features --features ...`) does
# not have to know about them.
exclude = [
"examples/hello_world",
]
resolver = "2"
30 changes: 30 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,36 @@ docker-test-current:
${DOCKER_REPO}/rust_icu_testenv-current:${USED_BUILDENV_VERSION}
.PHONY: docker-test-current

# Builds and runs the standalone examples (examples/*) inside the dockerized
# test environment. The example crates are excluded from the workspace, so the
# regular docker-test target does not touch them; this target gives the
# presubmits a way to confirm the documented example actually compiles, runs and
# prints the expected output. It overrides the image entrypoint and drives
# cargo directly because the examples are built with default features, unlike
# the feature-matrix runs.
docker-test-example:
mkdir -p ${CARGO_TARGET_DIR}
echo top_dir: ${TOP_DIR}
echo pwd: $(shell pwd)
docker run ${TTY} \
--user=${UID}:${GID} \
--volume=${TOP_DIR}:/src/rust_icu \
--volume=${CARGO_TARGET_DIR}:/build/cargo \
--volume=${LOGNAME_HOME}/.cargo:/usr/local/cargo \
--env="RUST_ICU_MAJOR_VERSION_NUMBER=${RUST_ICU_MAJOR_VERSION_NUMBER}" \
--env="RUST_BACKTRACE=full" \
--entrypoint="/bin/bash" \
${DOCKER_REPO}/${DOCKER_TEST_ENV}:${USED_BUILDENV_VERSION} \
"-l" "-c" "set -eo pipefail; \
for example in /src/rust_icu/examples/*/; do \
( cd \"$$example\" \
&& env LD_LIBRARY_PATH=/usr/local/lib \
cargo test --target-dir=/build/cargo \
&& env LD_LIBRARY_PATH=/usr/local/lib \
cargo run --target-dir=/build/cargo ); \
done"
.PHONY: docker-test-example

# Test with the homebrew version of icu4c on macOS, running bindgen natively
# using the ICU headers provided by Homebrew and the clang toolchain from Xcode.
# Two passes: dynamic linking (default) and static linking.
Expand Down
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,27 @@ Crate
[rust_icu_utext](https://crates.io/crates/rust_icu_utext) | Text operations. Implements [`utext.h`](https://unicode-org.github.io/icu-docs/apidoc/released/icu4c/utext_8h.html) C API header from the ICU library.
[rust_icu_utrans](https://crates.io/crates/rust_icu_utrans) | Transliteration support. Implements [`utrans.h`](https://unicode-org.github.io/icu-docs/apidoc/released/icu4c/utrans_8h.html) C API header from the ICU library.

# Hello, World!

A complete, runnable "Hello, World!" lives in
[`examples/hello_world`](examples/hello_world). It shows locale-aware
[MessageFormat](http://userguide.icu-project.org/formatparse/messages) string
formatting (MessageFormat is *not* XLIFF; it is a pattern-based template
language built into ICU). Run it with:

```sh
cd examples/hello_world
cargo run
# => Hello, World!
```

The example's [`Cargo.toml`](examples/hello_world/Cargo.toml) lists the
dependencies you need (`rust_icu_common`, `rust_icu_uloc`, `rust_icu_umsg`,
`rust_icu_ustring`), and [`src/main.rs`](examples/hello_world/src/main.rs)
contains the code. See the
[`rust_icu_umsg` crate docs](https://docs.rs/rust_icu_umsg) for more advanced
patterns (numbers, dates, plural rules, etc.).

# Limitations

The generated rust language binding methods of today limit the availability of
Expand Down
29 changes: 29 additions & 0 deletions examples/hello_world/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
[package]
name = "hello_world"
version = "0.0.0"
edition = "2018"
license = "Apache-2.0"
publish = false
description = "Minimal rust_icu MessageFormat 'Hello, World!' example."

# This example is intentionally kept out of the top-level workspace (see the
# `exclude` entry in ../../Cargo.toml) so it can be built on its own:
#
# cd examples/hello_world
# cargo run
#
# The dependencies below use `path` so the example always builds against the
# crates in this repository. A downstream user would instead depend on the
# published crates, e.g.:
#
# [dependencies]
# rust_icu_common = "5.6"
# rust_icu_uloc = "5.6"
# rust_icu_umsg = "5.6"
# rust_icu_ustring = "5.6"

[dependencies]
rust_icu_common = { path = "../../rust_icu_common" }
rust_icu_uloc = { path = "../../rust_icu_uloc" }
rust_icu_umsg = { path = "../../rust_icu_umsg" }
rust_icu_ustring = { path = "../../rust_icu_ustring" }
50 changes: 50 additions & 0 deletions examples/hello_world/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# `rust_icu` Hello, World!

A minimal, runnable example of locale-aware
[MessageFormat](http://userguide.icu-project.org/formatparse/messages) string
formatting with `rust_icu`. `MessageFormat` is a pattern-based template
language built into ICU that lets you embed formatted numbers, dates, and
plurals directly inside a message string. It is *not* XLIFF.

## Run it

You need a working ICU installation, the same as for the rest of `rust_icu`
(see the [top-level README](../../README.md)). Then:

```sh
cd examples/hello_world
cargo run
```

Expected output:

```text
Hello, World!
```

## Verified by presubmits

This example is not just documentation. The project's CI builds and runs it on
every push and pull request, across the same ICU versions as the rest of the
test matrix. The `make docker-test-example` target builds the crate inside the
dockerized test environment, runs `cargo test` (which asserts that the formatted
output equals `Hello, World!`), and then runs the binary with `cargo run`. If
the example ever stops compiling or stops producing the expected output, CI
fails.

You can reproduce the presubmit locally with:

```sh
make DOCKER_TEST_ENV=rust_icu_testenv-77 RUST_ICU_MAJOR_VERSION_NUMBER=77 docker-test-example
```

## What it shows

- [`Cargo.toml`](Cargo.toml) — the dependencies required to format a message:
`rust_icu_common`, `rust_icu_uloc`, `rust_icu_umsg`, and `rust_icu_ustring`.
- [`src/main.rs`](src/main.rs) — building a `UMessageFormat` from a pattern and
a locale, then formatting a positional argument with the
[`message_format!`](https://docs.rs/rust_icu_umsg) macro.

See the [`rust_icu_umsg` crate docs](https://docs.rs/rust_icu_umsg) for more
advanced patterns (numbers, dates, plural rules, etc.).
63 changes: 63 additions & 0 deletions examples/hello_world/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// Copyright 2026 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! A minimal "Hello, World!" example for `rust_icu`.
//!
//! It uses [`rust_icu_umsg`] (ICU `MessageFormat`) to format a locale-aware
//! message. `MessageFormat` is a pattern-based template language built into
//! ICU; it is *not* XLIFF. Run it with:
//!
//! ```text
//! cargo run
//! ```
//!
//! The formatting logic lives in [`greet`] so it can be exercised by a unit
//! test (see the bottom of this file). That test is what lets the project's
//! presubmits confirm the example actually produces the expected output.

use rust_icu_common as common;
use rust_icu_uloc as uloc;
use rust_icu_umsg::{self as umsg, message_format};
use rust_icu_ustring as ustring;
use std::convert::TryFrom;

/// Formats a locale-aware "Hello, World!" using ICU `MessageFormat`.
fn greet() -> Result<String, common::Error> {
// Choose a locale. Formatting of numbers, dates and plurals is driven by
// the locale's ICU data.
let loc = uloc::ULoc::try_from("en-US")?;

// A MessageFormat pattern. `{0}` is the first positional argument.
let pattern = ustring::UChar::try_from("Hello, {0}!")?;
let fmt = umsg::UMessageFormat::try_from(&pattern, &loc)?;

// Bind a value to each positional parameter and format the message.
let name = ustring::UChar::try_from("World")?;
message_format!(fmt, { name => String })
}

fn main() -> Result<(), common::Error> {
println!("{}", greet()?); // Hello, World!
Ok(())
}

#[cfg(test)]
mod tests {
use super::greet;

#[test]
fn formats_hello_world() {
assert_eq!(greet().expect("formatting should succeed"), "Hello, World!");
}
}
Loading