@@ -82,6 +82,66 @@ echo $formatphp->formatMessage([
8282]);
8383```
8484
85+ ### Rich Text Formatting (Use of Tags in Messages)
86+
87+ While the ICU message syntax does not prohibit the use of HTML tags in formatted
88+ messages, HTML tags provide an added level of difficulty when it comes to parsing
89+ and validating ICU formatted messages. By default, FormatPHP does not support
90+ HTML tags in messages.
91+
92+ Instead, [ like FormatJS] ( https://formatjs.io/docs/core-concepts/icu-syntax#rich-text-formatting ) ,
93+ we support embedded rich text formatting using custom tags and callbacks. This
94+ allows developers to embed as much text as possible so sentences don't have to
95+ be broken up into chunks. These are not HTML or XML tags, and attributes are
96+ not supported.
97+
98+ ``` php
99+ $formatphp->formatMessage([
100+ 'id' => 'priceMessage',
101+ 'defaultMessage' => <<<'EOD'
102+ Our price is <boldThis >{price, number, ::currency/USD precision-integer}</boldThis >
103+ with <link >{pct, number, ::percent} discount</link >
104+ EOD,
105+ ], [
106+ 'price' => 29.99,
107+ 'pct' => 2.5,
108+ 'boldThis' => fn ($text) => "<strong >$text</strong >",
109+ 'link' => fn ($text) => "<a href =\ " /discounts/1234\" >$text</a >",
110+ ]);
111+ ```
112+
113+ For an ` en-US ` locale, this will produce a string similar to the following:
114+
115+ Our price is <strong>$30</strong> with <a href="/discounts/1234">2.5% discount</a>
116+
117+ For rich text elements used throughout your application, you may provide a map
118+ of tag names to rich text formatting functions, when configuring FormatPHP.
119+
120+ ``` php
121+ $config = new Config(
122+ locale: new Intl\Locale('en-US'),
123+ defaultRichTextElements: [
124+ 'em' => fn ($text) => "<em class =\ " myClass\" >$text</em >",
125+ 'strong' => fn ($text) => "<strong class =\ " myClass\" >$text</strong >",
126+ ],
127+ );
128+ ```
129+
130+ Using this approach, consider the following formatted message:
131+
132+ ``` php
133+ $formatphp->formatMessage([
134+ 'id' => 'welcome',
135+ 'defaultMessage' => 'Welcome, <strong ><em >{name}</em ></strong >',
136+ ], [
137+ 'name' => 'Sam',
138+ ]);
139+ ```
140+
141+ It will produce a string similar to the following:
142+
143+ Welcome, <strong class="myClass"><em class="myClass">Sam</em></strong>
144+
85145### Using MessageLoader to Load Messages
86146
87147We also provide a message loader to load translation strings from locale files
@@ -134,7 +194,10 @@ application source code, saving them to JSON files that your translation
134194management system can use.
135195
136196``` shell
137- ./vendor/bin/formatphp extract --out-file=locales/en.json ' src/**/*.php' ' src/**/*.phtml'
197+ ./vendor/bin/formatphp extract \
198+ --out-file=locales/en.json \
199+ ' src/**/*.php' \
200+ ' src/**/*.phtml'
138201```
139202
140203In order for message extraction to function properly, we have a few rules that
@@ -167,6 +230,124 @@ At least one of `id` or `defaultMessage` must be present.
167230> ```
168231> --additional-function-names='formatMessage, myCustomFormattingFunction'
169232> ```
233+ >
234+ > To see all available options, view the command help with `formatphp help extract`.
235+
236+ ### Pseudo Locales
237+
238+ Pseudo locales provide a way to test an application with various types of
239+ characters and string widths. FormatPHP provides a tool to convert any locale
240+ file to a pseudo locale for testing purposes.
241+
242+ Given the English message `my name is {name}`, the following table shows how
243+ each supported pseudo locale will render this message.
244+
245+ | Locale | Message |
246+ |---------|----------------------------------------------|
247+ | `en-XA` | `ṁẏ ńâṁè íś {name}` |
248+ | `en-XB` | `[!! ṁẏ ńâṁṁṁè íííś !!]{name}` |
249+ | `xx-AC` | `MY NAME IS {name}` |
250+ | `xx-HA` | `[javascript]my name is {name}` |
251+ | `xx-LS` | `my name is {name}SSSSSSSSSSSSSSSSSSSSSSSSS` |
252+
253+ To convert a locale to a pseudo locale, use the `formatphp pseudo-locale` command.
254+
255+ ```shell
256+ ./vendor/bin/formatphp pseudo-locale \
257+ --out-file locales/en-XA.json \
258+ locales/en.json \
259+ en-XA
260+ ```
261+
262+ > ℹ️ To see all available options, view the command help with
263+ > ` formatphp help pseudo-locale ` .
264+
265+ ## TMS Support
266+
267+ A [ translation management system] ( https://en.wikipedia.org/wiki/Translation_management_system ) ,
268+ or TMS, allows translators to use your default locale file to create translations
269+ for all the other languages your application supports. To work with a TMS, you
270+ will extract the formatted strings from your application to send to the TMS.
271+ Often, a TMS will specify a particular document format they require.
272+
273+ Out of the box, FormatPHP supports the following formatters for integration with
274+ third-party TMSes. Supporting a TMS does not imply endorsement of that
275+ particular TMS.
276+
277+ | TMS | ` --format ` |
278+ | --------------------------------------------------------------------------------------| -------------|
279+ | [ Crowdin Chrome JSON] ( https://support.crowdin.com/file-formats/chrome-json/ ) | ` crowdin ` |
280+ | [ Lingohub] ( https://lingohub.com/developers/resource-files/json-localization/ ) | ` simple ` |
281+ | [ locize] ( https://docs.locize.com/integration/supported-formats#json-flatten ) | ` simple ` |
282+ | [ Phrase] ( https://help.phrase.com/help/simple-json ) | ` simple ` |
283+ | [ SimpleLocalize] ( https://simplelocalize.io/docs/integrations/format-js-cli/ ) | ` simple ` |
284+ | [ Smartling ICU JSON] ( https://help.smartling.com/hc/en-us/articles/360008000733-JSON ) | ` smartling ` |
285+
286+ Our default formatter is ` formatphp ` , which mirrors the output of default
287+ formatter for FormatJS.
288+
289+ ### Custom Formatters
290+
291+ You may provide your own formatter using our interfaces. You will need to
292+ create a writer for the format. Optionally, you may create a reader, if using
293+ our message loader or the ` formatphp pseudo-locale ` command with the
294+ ` --in-format ` option.
295+
296+ * The writer must implement ` FormatPHP\Format\WriterInterface ` or be a callable
297+ of the shape ` callable(FormatPHP\DescriptorCollection, FormatPHP\Format\WriterOptions): mixed[] ` .
298+ * The reader must implement ` FormatPHP\Format\ReaderInterface ` or be a
299+ callable of the shape ` callable(mixed[]): FormatPHP\MessageCollection ` .
300+
301+ To use your custom writer with ` formatphp extract ` , pass the fully-qualified
302+ class name to ` --format ` , or a path to a script that returns the callable.
303+
304+ For example, given the script ` my-writer.php ` with the contents:
305+
306+ ``` php
307+ <?php
308+
309+ use FormatPHP\DescriptorCollection;
310+ use FormatPHP\Format\WriterOptions;
311+
312+ require_once 'vendor/autoload.php';
313+
314+ /**
315+ * @return mixed[]
316+ */
317+ return function(DescriptorCollection $descriptors, WriterOptions $options): array {
318+ // Custom writer logic to create an array of data we will write
319+ // as JSON to a file, which your TMS will be able to use.
320+ };
321+ ```
322+
323+ You can call ` formatphp extract ` like this:
324+
325+ ``` shell
326+ ./vendor/bin/formatphp extract \
327+ --format=' path/to/my-writer.php' \
328+ --out-file=locales/en.json \
329+ ' src/**/*.php'
330+ ```
331+
332+ To use a custom reader with the message loader:
333+
334+ ``` php
335+ $messageLoader = new MessageLoader(
336+ // The path to your locale JSON files (i.e., en.json, es.json, etc.).
337+ messagesDirectory: '/path/to/app/locales',
338+ // The configuration object created earlier.
339+ config: $config,
340+ // Pass your custom reader through the formatReader parameter.
341+ formatReader: MyCustomReader::class,
342+ );
343+ ```
344+
345+ The ` formatReader ` parameter accepts the following:
346+
347+ * Fully-qualified class name for a class that implements ` FormatPHP\Format\ReaderInterface `
348+ * An already-instantiated instance object of ` FormatPHP\Format\ReaderInterface `
349+ * A callable with the shape ` callable(mixed[]): FormatPHP\MessageCollection `
350+ * The path to a script that returns a callable with this shape
170351
171352## Contributing
172353
0 commit comments