Skip to content

Usage: Basics

Adrian edited this page Mar 1, 2024 · 10 revisions

Making Messages Available

setting up the registry

The MessageRegistry is a centralized collection of resource bundles to be made available to your application. During your startup phase, you should set the default locale and register any bundles that are needed.

use at\peekaboo\MessageRegistry;

MessageRegistry::$defaultLocale = "en_US";

// @see https://php.net/ResourceBundle.__construct
$messages = new ResourceBundle($locale, $bundle);
MessageRegistry::register($messages);

Messages can be organized in groups, for example, to allow the usage of specific bundles for specific messages. This can make lookups more efficient (i.e., because you're no longer searching all bundles), and prevent key collisions (e.g., between different modules). The use of group names is especially recommended for libraries, plugins, etc. intended for use in other projects.

$specialMessages = new ResourceBundle($locale, $specializedBundle);
MessageRegistry::register($specialMessages, "special");

adding messages to classes

While the MessageRegistry can be used ad-hoc, the recommended approach is to make your classes implement HasMessages. This allows messages to be more clearly defined and streamlines usage.

use at\peekaboo\ {
  HasMessages,
  MakesMessages
};

class Foo implements HasMessages {
  use MakesMessages;
}

MessageRegistry::register(new ResourceBundle("en_US", "your/messages/"));
echo (new Foo())->makeMessage("hello-world");
// looks up format from the registry and presumably outputs something like "hello, world!"

adding default messages

HasMessages classes can provide their own "fallback" messages, for use when a matching message format is not found in the registry. These are defined as a class const MESSAGES, which is expected to be an array of message formats structured like a ResourceBundle.

class Bar implements HasMessages {
  use MakesMessages;

  public const MESSAGES = [
    "my-own" => [
      "message" => "well, hello there"
    ]
  ];
}

echo (new Bar())->makeMessage("my-own.message");
// assuming `my-own.message` is not found in any registered bundle, outputs "well, hello there"

adding context

Of course, one of the biggest benefits of using ICU message formatting is the ability to add context to your messages. If you have the intl extension enabled, your messages can include {tokens} for simple interpolation, formatting instructions, or complex expressions.

class Baz implements HasMessages {
  use MakesMessages;

  public const MESSAGES = [
    "welcome" => "welcome to the {place}, we've got fun and games"
  ];
}

echo (new Baz())->makeMessage("welcome", ["place" => "jungle"]);
// outputs "welcome to the jungle, we've got fun and games"

@see ICU documentation for more details and how to build more complex formats.

specifying locale and narrowing format lookups

If you want to format a particular message using a local other than the registry's default locale, you can pass it along as the third argument to makeMessage():

echo (new Baz())->makeMessage("welcome", ["place" => "jungle"], "pt_PT");

The fourth argument is the bundle group to restrict lookup to - e.g., if you've registered a particular bundle with a name and want to use those formats specifically. Peekaboo will still fall back on the default group if the message is not found.

echo (new Baz())->makeMessage("welcome", ["place" => "jungle"], "pt_PT", "special");

Usage Without the intl Extension

Peekaboo can still provide basic message interpolation if the intl extension is not available, with the limitation that formatting instructions and complex expressions are ignored. This can result in "ugly" messages if you're not careful about what formats and context values are used. If you need these features, you should ensure that the intl extension is available.

A good compromise approach is to use ICU bundles when intl is available, and rely on defining fallback MESSAGES (with simpler formats) for when it is not - e.g., your MESSAGES should not include things like {when, date, medium} or {count, plural, offset:1 ...} or {status, select, pending { ... } ...} and so on. Peekaboo will try to "eat" these extra instructions, but this might not always work (and might not result in a sensible message even when it does).

Clone this wiki locally