Compile an ICU Resource Bundle with genrb
You will finish with a small ICU resource source file compiled into a binary .res bundle, plus checks that catch the usual path and locale-name mistakes. The examples were run with genrb from Debian package icu-devtools version 74.2-1ubuntu3.1, reporting ICU version 74.2.
The route
Jump straight to the step you need, or tick off Done means at the end.
Allow about fifteen minutes. You need a shell, the ICU development tools package, and a writable build directory. This guide compiles a test bundle in a temporary directory. It does not install the result or replace system ICU data, so no elevated privileges are needed.
1. Check the installed tool
Start with the binary that will actually process your files:
$ command -v genrb
/usr/bin/genrb
$ genrb --version
genrb version 56 (ICU version 74.2).
$ dpkg-query -W -f='\${Package} \${Version}\n' icu-devtools
icu-devtools 74.2-1ubuntu3.1
The first number in the version output is genrb's tool version. The ICU data and library version is the useful compatibility marker here: 74.2. The installed command also has options that are newer or more detailed than the local manual page, so use genrb --help when moving this recipe to another ICU package.
Checkpoint
If command -v finds nothing, install the distribution's ICU development-tools package through its normal package-management process. Do not work around a missing command by copying a binary from another host.
2. Make a minimal source bundle
A resource source file is text. Its filename is only an input name; genrb uses the locale name declared in the resource when choosing the output base name. This example declares the root bundle and gives it one string and one integer:
$ work=/tmp/genrb-example
$ mkdir -p "$work/src" "$work/out"
$ printf '%s\n' 'root:table { greeting:string { "Hello from genrb" } count:int { 3 } }' > "$work/src/example.txt"
$ sed -n '1p' "$work/src/example.txt"
root:table { greeting:string { "Hello from genrb" } count:int { 3 } }
Use a real source tree in a project rather than /tmp once the syntax is established. Keep the source under version control and treat the destination as generated output. The quoted shell variable prevents spaces in a future path from changing the command's arguments.
For a locale bundle, put the locale in the resource declaration, for example en_GB:table { ... }. Do not assume that en_GB.txt alone forces an en_GB.res result: genrb names the result from the locale found in the resource file.
3. Compile to a binary bundle
Pass the source and destination directories explicitly. This makes the command reproducible and avoids accidentally writing into the system ICU data directory:
$ genrb --sourcedir "$work/src" --destdir "$work/out" example.txt
$ find "$work/out" -maxdepth 1 -type f -printf '%f\n'
root.res
$ file "$work/out/root.res"
/tmp/genrb-example/out/root.res: data
The output is binary, so file will not show your strings. The useful checks are the exact filename, its existence, and a zero exit status from genrb. ICU can read the resulting root.res, and pkgdata can use it as an input when building a larger ICU data archive.
Checkpoint
You should now have exactly one generated file, root.res. If you get a different base name, inspect the locale declaration rather than renaming the file by hand.
4. Make input encoding explicit when needed
Without an encoding option, the manual describes the default as the system invariant codepage. A UTF-8, UTF-16BE or UTF-16LE byte-order mark is detected automatically. If your source is known to be UTF-8 but has no BOM, state that fact in the command instead of relying on the host default:
$ genrb --encoding UTF-8 \
--sourcedir "$work/src" \
--destdir "$work/out" \
example.txt
Encoding is about reading the source file. It does not change the fact that the normal output is a binary .res file. If a non-ASCII string fails to compile or is displayed incorrectly by a consumer, check the file's actual encoding and the encoding option before changing the resource syntax.
5. Inspect a compilation without drowning in output
Use verbose mode when a bundle contains several resources or when you need to see which file is being parsed:
$ genrb --verbose --sourcedir "$work/src" --destdir "$work/out" example.txt
Processing file "/tmp/genrb-example/src/example.txt"
parsing table (null) at line 1
resource greeting at line 1
string greeting at line 1
resource count at line 1
integer count at line 1
The wording and line details are diagnostic output, not a stable file format. For a quiet build, the installed command also accepts --quiet; use it only after ordinary warnings have been dealt with. A warning suppressed by a quiet build is still a warning you may need to investigate.
6. Generate Java only when ICU4J needs it
The installed tool can emit a Java ListResourceBundle source file with --write-java. The optional encoding argument is easy to misread: if the next token is not another option, genrb may treat it as the encoding. Omit the optional value by placing another option immediately after -j:
$ mkdir -p "$work/java"
$ genrb -j -s "$work/src" -d "$work/java" example.txt
$ find "$work/java" -maxdepth 1 -type f -printf '%f\n'
LocaleElements.java
The default Java output uses ASCII and Unicode escape sequences such as \u0041. To select an encoding, put it directly after -j, then put the source-directory option after it:
$ mkdir -p "$work/java-utf8"
$ genrb -j UTF-8 -s "$work/src" -d "$work/java-utf8" example.txt
$ test -s "$work/java-utf8/LocaleElements.java" && echo 'Java source written'
Java source written
The default bundle name for this mode is LocaleElements. That is a Java source naming convention, not evidence that the input locale was changed. Confirm the generated package and class are suitable for the ICU4J code that will compile them before committing the file.
7. Diagnose failures before changing directories
A missing source file is a path error. Reproduce the check with a deliberately absent name:
$ genrb --sourcedir "$work/src" --destdir "$work/out" missing.txt
couldn't open file /tmp/genrb-example/src/missing.txt
$ printf 'exit status: %s\n' "$?"
exit status: 4
The temporary path in the message will differ on your machine. Check the source directory, filename and case first. The destination directory must also exist and be writable. Do not use sudo as a first response: it can hide an incorrect path and create root-owned build artefacts.
For repeatable builds, keep --sourcedir and --destdir explicit. If you omit them, genrb falls back to ICU data locations, including the ICU_DATA environment variable. Some ICU tools expect a trailing slash when ICU_DATA is set, so inspect it with printf '%s\n' "$ICU_DATA" before relying on that default. A missing or wrong data directory matters especially when processing collation overrides.
8. Clean up the test output
The example changed only the temporary directory. Once you have checked the files, remove that specific directory, not a broad path:
$ rm -rf -- "$work"
$ test ! -e "$work" && echo 'temporary build removed'
temporary build removed
This is the one destructive command in the guide. Confirm that work contains only disposable test output before running it. For a project build, delete only generated files according to that project's documented clean target, and keep the source bundle.
Done means
genrb --versionidentified the installed ICU version.- A resource source file compiled to a
.resfile in an explicit destination. - The output filename was checked against the locale declared in the resource.
- Input encoding and ICU data directory defaults are understood rather than accidental.
- Java output was generated only when required, with its optional encoding handled deliberately.
- A missing-input failure was recognised as a path problem, without reaching for elevated privileges.