Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add explanation for Component and ComponentContents #4

Merged
merged 17 commits into from
Aug 4, 2023
Merged
9 changes: 6 additions & 3 deletions docs/concepts/internationalization.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ Localization methods
:::caution
A common issue is having the server localize for clients. The server can only localize in its own locale, which does not necessarily match the locale of connected clients.

To respect the language settings of clients, the server should have clients localize text in their own locale using `TranslatableComponent` or other methods preserving the language neutral translation keys.
To respect the language settings of clients, the server should have clients localize text in their own locale using `TranslatableContents` or other methods preserving the language neutral translation keys.
:::

### `net.minecraft.client.resources.language.I18n` (client only)
Expand All @@ -56,13 +56,16 @@ To respect the language settings of clients, the server should have clients loca

`TranslatableContents` is a `ComponentContents` that is localized and formatted lazily. It is very useful when sending messages to players because it will be automatically localized in their own locale.

The first parameter of the `TranslatableContents(String, Object...)` constructor is a translation key, and the rest are used for formatting. The only supported format specifiers are `%s` and `%1$s`, `%2$s`, `%3$s` etc. Formatting arguments may be `Component`s that will be inserted into the resulting formatted text with all their attributes preserved.
The first parameter of the `TranslatableContents(String, Object...)` constructor is a translation key, and the rest are used for [formatting].

A `MutableComponent` can be created using `Component#translatable` by passing in the `TranslatableContents`'s parameters. It can also be created using `MutableComponent#create` by passing in the `ComponentContents` itself.
tmvkrpxl0 marked this conversation as resolved.
Show resolved Hide resolved
Read [components] for more details.

### `TextComponentHelper`

- `createComponentTranslation(CommandSource, String, Object...)` creates a localized and formatted `MutableComponent` depending on a receiver. The localization and formatting is done eagerly if the receiver is a vanilla client. If not, the localization and formatting is done lazily with a `Component` containing `TranslatableContents`. This is only useful if the server should allow vanilla clients to connect.
- `createComponentTranslation(CommandSource, String, Object...)` is useful for messaging between clients and server, where vanilla clients can also join. If the receiver is vanilla client, the method will eagerly localize and format the provided translation key in sender's locale, or American English if no locale is loaded, as modded server may allow vanilla clients to join, and they will lack localization data required to do it itself. Otherwise, the method will create component with `TranslatableContents`.
tmvkrpxl0 marked this conversation as resolved.
Show resolved Hide resolved

[langs]: https://minecraft.fandom.com/wiki/Language#Languages
[converter]: https://tterrag.com/lang2json/
[formatting]: ../misc/components.md#text-formatting
[components]: ../misc/components.md
128 changes: 128 additions & 0 deletions docs/misc/components.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
Text Components
==================

`Component` is a holder for text which can be formatted and chained with other components.
The subtype `MutableComponent` is used to apply formats and chain other components.
tmvkrpxl0 marked this conversation as resolved.
Show resolved Hide resolved
A Component can be created using one of the available static helpers:
tmvkrpxl0 marked this conversation as resolved.
Show resolved Hide resolved

| Method Name | Description |
|----------------|-----------------------------------------------------------------------------------------------------------------------|
| `literal` | it creates component which simply wraps passed in text. |
| `nullToEmpty` | it's same as `#literal` except it creates empty component if null has been passed |
| `translatable` | it creates component which is represented as localized text to user, read [internationalization] for more details. |
| `empty` | it creates empty component |
| `keybind` | it creates component which is represented as name of current Keyboard key of passed keybind. |
| `nbt` | it creates component for representing nbt data specified by `path` inside `dataSource` |
| `score` | it creates component for representing `objective`'s score of entity specified by [entity selector][selectors] `name`. |
| `selector` | it creates component for displaying list of names of entities selected by [entity selector][selectors] `pattern`. |
tmvkrpxl0 marked this conversation as resolved.
Show resolved Hide resolved

These helpers create [`ComponentContents`][content] and wrap it in `MutableComponent`
tmvkrpxl0 marked this conversation as resolved.
Show resolved Hide resolved

Applying Style
--------------

Components can be formatted (e.g., bold, click actions, color) via `Style`s.
They're immutable objects which create copy of themselves instead when modified.
tmvkrpxl0 marked this conversation as resolved.
Show resolved Hide resolved
You can reconfigure `Style.EMPTY` to your preferences.
tmvkrpxl0 marked this conversation as resolved.
Show resolved Hide resolved

`Style`'s configurations can have empty values and multiple styles can be merged together with `#applyTo(Style other)`,
with `other` overriding all empty configurations of `this`
tmvkrpxl0 marked this conversation as resolved.
Show resolved Hide resolved

After configuring style to your preference,
you can apply it to your component with either `MutableComponent#setStyle` for overwriting,
or `#withStyle` for merging with previous one.
tmvkrpxl0 marked this conversation as resolved.
Show resolved Hide resolved

Here's an example of how you can stylize your component:
tmvkrpxl0 marked this conversation as resolved.
Show resolved Hide resolved
```java
// Creates MutableComponent wrapping literal "Hello!"
MutableComponent text = Component.literal("Hello!");

// Copies empty style and sets color to blue and makes it italic
Style blueItalic = Style.EMPTY
.withColor(0x0000FF)
.withItalic(true);

// Copies empty style and sets color to red
Style red = Style.EMPTY
.withColor(0xFF0000);

// Copies empty style and makes it bold
Style bold = Style.EMPTY
.withBold(true);

// Copies empty style and makes it both underlined and strikethrough
Style doubleLines = Style.EMPTY
.withUnderlined(true)
.withStrikethrough(true);

// Sets style of the text to be blue and italic
text.setStyle(blueItalic);

// Overwrites blue and italic style to be red, bold, underlined, and strikethrough
text.withStyle(red).withStyle(bold).withStyle(doubleLines);
```
This results in red, bold text with two lines as shown in the [image][red_hello]
tmvkrpxl0 marked this conversation as resolved.
Show resolved Hide resolved

Chaining Components
-------------------

`MutableComponent` can have additional components chained as siblings with `MutableComponent#append`. Chained components can be retrieved with `MutableComponent#getSiblings`.
tmvkrpxl0 marked this conversation as resolved.
Show resolved Hide resolved

`Component` stores its siblings like a tree, and its style is merged with those of siblings as shown in the [image][tree].
tmvkrpxl0 marked this conversation as resolved.
Show resolved Hide resolved
The tree is traversed in preorder.

Below example will create a component with the same structure as the above image:
tmvkrpxl0 marked this conversation as resolved.
Show resolved Hide resolved
```java
// Create text only components
MutableComponent first = Component.literal("first ");
MutableComponent second = Component.literal("second ");
MutableComponent third = Component.literal("third ");
MutableComponent fourth = Component.literal("fourth ");
MutableComponent fifth = Component.literal("fifth ");
MutableComponent sixth = Component.literal("sixth ");
MutableComponent seventh = Component.literal("seventh ");

// Create components with style
MutableComponent red = Component.litearl("red ").withStyle(Style.EMPTY.withColor(0xFF0000));
MutableComponent blue = Component.literal("blue ").withStyle(Style.EMPTY.withColor(0x0000FF));
MutableComponent bold = Component.literal("bold ").withStyle(Style.EMPTY.withBold(true));

// Structure created components in the same way as the image
red.append(first).append(blue).append(seventh);
blue.append(second).append(third).append(bold);
bold.append(fourth).append(fifth).append(sixth);
```
[Here's how it looks like in-game][style_annotated]
tmvkrpxl0 marked this conversation as resolved.
Show resolved Hide resolved
tmvkrpxl0 marked this conversation as resolved.
Show resolved Hide resolved

`ComponentContents`
-------------------

Component's text contents are handled by `ComponentContents`, it holds data and defines how to represent it as text.
Notably, subtype `TranslatableContents` not only supports localization but also text formatting.
tmvkrpxl0 marked this conversation as resolved.
Show resolved Hide resolved

### Text Formatting

Text formatting is the process of inserting data as text into predefined larger text.
It can be used for displaying coordinate with x, y, z annotation, showing number with respectful units alongside, etc.
tmvkrpxl0 marked this conversation as resolved.
Show resolved Hide resolved
Usually special notation called **format specifiers** are used for indicating where a text can be inserted into.
tmvkrpxl0 marked this conversation as resolved.
Show resolved Hide resolved

`TranslatableContents` uses two types of format specifiers: `%s` and `%1$s`, `%2$s`, `%3$s`.
tmvkrpxl0 marked this conversation as resolved.
Show resolved Hide resolved
Its `args` are inserted in place of format specifiers.
This feature is useful as order of information in various languages varies.
tmvkrpxl0 marked this conversation as resolved.
Show resolved Hide resolved

`%s` is replaced with elements of `args` in order they appear, i.e., First `%s` is replaced with a first element of `args`, and so on.
tmvkrpxl0 marked this conversation as resolved.
Show resolved Hide resolved
tmvkrpxl0 marked this conversation as resolved.
Show resolved Hide resolved
`%n$s` is positional specifier; they can specify which element will replace them with number `n`.
tmvkrpxl0 marked this conversation as resolved.
Show resolved Hide resolved
Here's an example of how format specifier works in practice:
* Formatting `x:%s y:%s z:%s` with `[1, 2, 3]` as `args` results in `x:1 y:2 z:3`
* Formatting `Time: %1$s ms` with `17` as `args` results in `Time: 17 ms`
* Formatting `Player name: %2$s, HP: %1$s` with `[10.2, Dev]` as `args` results in `Player name: Dev, HP: 10.2`

`args`'s elements may be `Component`s that will be inserted into the resulting formatted text with all their attributes preserved.
tmvkrpxl0 marked this conversation as resolved.
Show resolved Hide resolved
tmvkrpxl0 marked this conversation as resolved.
Show resolved Hide resolved

[internalization]: ../concepts/internationalization.md
[selectors]: https://minecraft.fandom.com/wiki/Target_selectors
[red_hello]: /img/component_red_hello.png
[style_annotated]: /img/component_style_annotated.png
[formatting]: #text-formatting
[tree]: /img/component_graph.png
[content]: #componentcontents
Binary file added static/img/component_graph.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added static/img/component_red_hello.png
tmvkrpxl0 marked this conversation as resolved.
Show resolved Hide resolved
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added static/img/component_style_annotated.png
tmvkrpxl0 marked this conversation as resolved.
Show resolved Hide resolved
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.