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
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 sending messages between clients and the server. If the receiver is a 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; the modded server may allow vanilla clients to join, and they will lack localization data required to localize the message itself. Otherwise, the method will create the component with `TranslatableContents`.

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

A `Component` is a holder for text which can be formatted and chained with other components via its subtype `MutableComponent`. A component can be created using one of the available static helpers:

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

A component's text contents are represented by `ComponentContents`. Notably, the subtype `TranslatableContents` not only supports [localization][internalization] but also [text formatting][formatting].

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

Components can be formatted (e.g., bold, click actions, color) via `Style`s. `Style`s are immutable, creating a new `Style` each time when modified. The empty style `Style#EMPTY` can be used as a base for configuration.

Multiple styles can be merged together with `#applyTo(Style other)`; `other` will override all non-configured of the current object.

After configuring a style, it can be applied to a component with either `MutableComponent#setStyle` for overwriting, or `#withStyle` for merging:
```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 creates a red, bold text with two lines:
![red_hello]

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

`MutableComponent#append` can chain multiple components together. Chained components can be retrieved with `MutableComponent#getSiblings`.

`Component` stores its siblings like a tree and is traversed in preorder; the parent style is merged with those of its siblings.
![tree]

The code below will create a component with the same structure in the above example:
```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);
```
![style_annotated]

Text Formatting
---------------

Text formatting is the process of inserting data as text into predefined larger text. It can be used for displaying coordinates, showing unit measurements, etc. **Format specifiers** are used for indicating where a text can be inserted.

`TranslatableContents` allows two types of format specifiers: `%s` and `%n$s`. The component uses the second parameter onwards, denoted as `args` , for holding what object to insert in place of a format specifier.

`%s` is replaced with elements of `args` in order they appear, i.e., the first `%s` is replaced with the first element of `args`, and so on.
`%n$s` is positional specifier; each positional specifier can denote which element in `args` will replace the specifier via the number `n`.
* 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`

Any `Component` element within `args` will be transformed into a formatted text string.

[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
[keymapping]: ./keymappings.md
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
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
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.