StringsUtil v1.1 Manual

Michael R Sweet

Copyright © 2022-2024 by Michael R Sweet

Contents

Introduction

StringsUtil provides a library for using Apple ".strings" localization files and a utility for managing those files. It is intended as a free, smaller, embeddable, and more flexible alternative to GNU gettext. Key features include:

Apple ".strings" Files

Apple ".strings" files are localization files used on macOS and iOS, as well as for localizing Internet Printing Protocol (IPP) attributes and values. The format consists of lines containing key/text pairs:

"key string" = "localized text value";

C-style comments can be included before a pair to provide information to a localizer:

/* A whitty comment for the localizer about this string */
"key string" = "localized text value";

Each ".strings" file represents a single language or locale.

Most open source software uses the GNU gettext library which supports a different ".po" file format:

# A whitty comment for the localizer about this string
msgid "key string"
msgstr "localized text value"

This format is "compiled" into binary ".mo" files that are typically stored in a system directory. Like ".strings" files, one ".po" file is used for every language or locale.

StringUtils supports importing and exporting ".po" files, when needed, but uses the Apple ".strings" format exclusively both on disk and in memory.

The stringsutil Tool

The stringsutil tool allows you to manage your ".strings" files. Create a ".strings" file by scanning source files in the current directory with the "scan" sub-command:

stringsutil -f base.strings scan *.[ch]

Create a ".po" file for external localizers to work with using the "export" sub-command:

stringsutil -f base.strings export es.po

When the localizer is done, use the "import" sub-command to import the strings from the ".po" file:

cp base.strings es.strings
stringsutil -f es.strings import es.po

Run the "report" sub-command to see how well the localizer did:

stringsutil -f base.strings report es.strings

When you have made source changes that affect the localization strings, use the "scan" sub-command again to update the base strings:

stringsutil -f base.strings scan *.[ch]

Then add those changes to the "es.strings" file with the "merge" sub-command:

stringsutil -f es.strings -c merge base.strings

The "translate" sub-command uses a LibreTranslate service to do a first-pass machine translation of your strings. For example, the following command will use a local Docker instance of LibreTranslate:

stringsutil -f es.strings -l es -T http://localhost:5000 translate base.strings

You also use the "export" command to produce a C header file containing a strings file that can be embedded in a program:

stringsutil -f es.strings export es_strings.h

Using the libsf Library

The libsf library has a single header file:

#include <sf.h>

Use the pkg-config command to get the proper compiler and linker options:

CFLAGS = `pkg-config --cflags libsf`
LIBS = `pkg-config --libs libsf`

cc -o myprogram `pkg-config --cflags libsf` myprogram.c `pkg-config --libs libsf`

A typical program will either have a directory containing ".strings" files installed on disk or a collection of header files containing ".strings" files as constant C strings. Call sfSetLocale to initialize the current locale and then sfRegisterDirectory or sfRegisterString to initialize the default localization strings:

// ".strings" files in "/usr/local/share/myapp/strings"...
#include <sf.h>

...

sfSetLocale();
sfRegisterDirectory("/usr/local/share/myapp/strings");


// ".strings" files in constant strings...
#include <sf.h>
#include "de_strings.h"
#include "es_strings.h"
#include "fr_strings.h"
#include "it_strings.h"
#include "ja_strings.h"

...

sfSetLocale();
sfRegisterString("de", de_strings);
sfRegisterString("es", es_strings);
sfRegisterString("fr", fr_strings);
sfRegisterString("it", it_strings);
sfRegisterString("ja", ja_strings);

Once the current local is initialized, you can use the sfPrintf and sfPuts functions to display localized messages. The SFSTR macro is provided by the <sf.h> header to identify strings for localization:

sfPuts(stdout, SFSTR("Usage: myprogram [OPTIONS] FILENAME"));

...

sfPrintf(stderr, SFSTR("myprogram: Expected '-n' value %d out of range."), n);

...

sfPrintf(stderr, SFSTR("myprogram: Syntax error on line %d of '%s'."), linenum, filename);

Functions

sfAddString

Add a localization string.

bool sfAddString(sf_t *sf, const char *key, const char *text, const char *comment);

Parameters

sf Strings
key Key
text Text
comment Comment or NULL for none

Return Value

true on success, false on error

Discussion

This function adds a localization string to the collection.

sfDelete

Free a collection of localization strings.

void sfDelete(sf_t *sf);

Parameters

sf Localization strings

Discussion

This function frees all memory associated with the localization strings.

sfFormatString

Format a localized string.

const char *sfFormatString(sf_t *sf, char *buffer, size_t bufsize, const char *key, ...);

Parameters

sf Localization strings or NULL for default
buffer Output buffer
bufsize Size of output buffer
key Printf-style format/key string
... Additional arguments as needed

Return Value

Formatted localized string

Discussion

This function formats a printf-style localized string using the specified localization strings. If no localization exists for the format (key) string, the original string is used. All snprintf format specifiers are supported.

The default localization strings ("sf" passed as NULL) are initialized using the sfSetLocale, sfRegisterDirectory, and sfRegisterString functions.

sfGetError

Get the last error message, if any.

const char *sfGetError(sf_t *sf);

Parameters

sf Localization strings

Return Value

Last error message or NULL for none

Discussion

This function returns the last error from sfLoadFile and sfLoadString, if any.

sfGetString

Lookup a localized string.

const char *sfGetString(sf_t *sf, const char *key);

Parameters

sf Localization strings or NULL for the default
key Key string

Return Value

Localized string

Discussion

This function looks up a localized string for the specified key string. If no localization exists, the key string is returned.

The default localization strings ("sf" passed as NULL) are initialized using the sfSetLocale, sfRegisterDirectory, and sfRegisterString functions.

sfHasString

Determine whether a string is localized.

bool sfHasString(sf_t *sf, const char *key);

Parameters

sf Localization strings
key Key string

Return Value

true if the string exists, false otherwise

Discussion

This function looks up a localization string, returning true if the string exists and false otherwise.

sfLoadFile

Load a ".strings" file.

bool sfLoadFile(sf_t *sf, const char *filename);

Parameters

sf Localization strings
filename File to load

Return Value

true on success, false on failure

Discussion

This function loads a ".strings" file. The "sf" argument specifies a collection of localization strings that was created using the sfNew function.

When loading the strings, any existing strings in the collection are left unchanged.

sfLoadString

Load a ".strings" file from a compiled-in string.

bool sfLoadString(sf_t *sf, const char *data);

Parameters

sf Localization strings
data Data to load

Return Value

true on success, false on failure

Discussion

This function loads a ".strings" file from a compiled-in string. The "sf" argument specifies a collection of localization strings that was created using the sfNew function.

When loading the strings, any existing strings in the collection are left unchanged.

sfNew

Create a new (empty) set of localization strings.

sf_t *sfNew(void);

Return Value

Localization strings

Discussion

This function creates a new (empty) set of localization strings. Use the sfLoadFile and/or sfLoadString functions to load localization strings.

sfPrintf

Print a formatted localized message followed by a newline.

void sfPrintf(FILE *fp, const char *message, ...);

Parameters

fp Output file
message Printf-style message
... Additional arguments as needed

Discussion

This function prints a formatted localized message followed by a newline to the specified file, typically stdout or stderr. You must call sfSetLocale and sfRegisterString or sfRegisterDirectory to initialize the message catalog that is used.

sfPuts

Print a localized message followed by a newline.

void sfPuts(FILE *fp, const char *message);

Parameters

fp Output file
message Message

Discussion

This function prints a localized message followed by a newline to the specified file, typically stdout or stderr. You must call sfSetLocale and sfRegisterString or sfRegisterDirectory to initialize the message catalog that is used.

sfRegisterDirectory

Register ".strings" files in a directory.

void sfRegisterDirectory(const char *directory);

Parameters

directory Directory of .strings files

Discussion

This function registers ".strings" files in a directory. You must call sfSetLocale first to initialize the current locale.

sfRegisterString

Register a ".strings" file from a compiled-in string.

void sfRegisterString(const char *locale, const char *data);

Parameters

locale Locale
data Strings data

Discussion

This function registers a ".strings" file from a compiled-in string. You must call sfSetLocale first to initialize the current locale.

sfRemoveString

Remove a localization string.

bool sfRemoveString(sf_t *sf, const char *key);

Parameters

sf Strings
key Key

Return Value

'true on success, false on error

Discussion

This function removes a localization string from the collection.

sfSetLocale

Set the current locale.

void sfSetLocale(void);

Discussion

This function calls setlocale to initialize the current locale based on the current user session, and then creates an empty message catalog that is filled by calls to sfRegisterDirectory and/or sfRegisterString.

Data Types

sf_t

Strings file

typedef struct _sf_s sf_t;