or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

audience-system.mdbooks-and-inventory.mdboss-bars.mdevents-and-interactivity.mdindex.mdnbt-data-components.mdresource-packs.mdsound-system.mdtext-components.mdtext-formatting.mdtitles-and-subtitles.mdtranslation-system.md

translation-system.mddocs/

0

# Translation System

1

2

Internationalization support with locale-based rendering, translation registries, and global translation management. Adventure's translation system enables multi-language support for text components.

3

4

## Capabilities

5

6

### Translatable Components

7

8

Components that display translated text based on the client's locale and registered translations.

9

10

```java { .api }

11

/**

12

* Component that displays translated text

13

*/

14

interface TranslatableComponent extends BuildableComponent<TranslatableComponent, TranslatableComponent.Builder> {

15

/**

16

* Gets the translation key

17

* @return the translation key

18

*/

19

String key();

20

21

/**

22

* Gets the translation arguments

23

* @return list of translation arguments

24

*/

25

List<TranslationArgument> arguments();

26

27

/**

28

* Gets the fallback string used when translation is not available

29

* @return the fallback string or null

30

*/

31

@Nullable String fallback();

32

33

/**

34

* Sets the translation key

35

* @param key the translation key

36

* @return component with new key

37

*/

38

TranslatableComponent key(String key);

39

40

/**

41

* Sets the translation arguments

42

* @param arguments the arguments

43

* @return component with new arguments

44

*/

45

TranslatableComponent arguments(List<? extends TranslationArgumentLike> arguments);

46

TranslatableComponent arguments(TranslationArgumentLike... arguments);

47

48

/**

49

* Sets the fallback string

50

* @param fallback the fallback string

51

* @return component with new fallback

52

*/

53

TranslatableComponent fallback(@Nullable String fallback);

54

}

55

```

56

57

### Translation Arguments

58

59

Arguments that can be passed to translatable components for parameter substitution.

60

61

```java { .api }

62

/**

63

* Argument for translatable components

64

*/

65

interface TranslationArgument {

66

/**

67

* Converts this argument to a component

68

* @return the component representation

69

*/

70

Component asComponent();

71

72

/**

73

* Converts this argument to a string

74

* @return the string representation

75

*/

76

String asString();

77

78

/**

79

* Creates a component argument

80

* @param component the component

81

* @return translation argument

82

*/

83

static TranslationArgument component(ComponentLike component);

84

85

/**

86

* Creates a string argument

87

* @param string the string

88

* @return translation argument

89

*/

90

static TranslationArgument string(String string);

91

92

/**

93

* Creates a numeric argument

94

* @param number the number

95

* @return translation argument

96

*/

97

static TranslationArgument numeric(Number number);

98

99

/**

100

* Creates a boolean argument

101

* @param bool the boolean value

102

* @return translation argument

103

*/

104

static TranslationArgument bool(boolean bool);

105

}

106

107

/**

108

* Objects that can be used as translation arguments

109

*/

110

interface TranslationArgumentLike {

111

/**

112

* Converts to a translation argument

113

* @return the translation argument

114

*/

115

TranslationArgument asTranslationArgument();

116

}

117

```

118

119

### Translator Interface

120

121

Core interface for translating keys to formatted messages based on locale.

122

123

```java { .api }

124

/**

125

* Interface for translating keys to messages

126

*/

127

interface Translator {

128

/**

129

* Translates a key to a message format

130

* @param key the translation key

131

* @param locale the target locale

132

* @return the message format or null if not found

133

*/

134

@Nullable MessageFormat translate(String key, Locale locale);

135

136

/**

137

* Creates an empty translator

138

* @return empty translator

139

*/

140

static Translator empty();

141

142

/**

143

* Creates a translator from a resource bundle

144

* @param bundle the resource bundle

145

* @return new translator

146

*/

147

static Translator translator(ResourceBundle bundle);

148

149

/**

150

* Creates a translator from a map of translations

151

* @param translations the translation map (key -> message)

152

* @return new translator

153

*/

154

static Translator translator(Map<String, String> translations);

155

}

156

```

157

158

### Global Translator

159

160

Global translation service that manages multiple translation sources.

161

162

```java { .api }

163

/**

164

* Global translation service for the entire application

165

*/

166

interface GlobalTranslator extends Translator {

167

/**

168

* Adds a translation source

169

* @param translator the translator to add

170

*/

171

void addSource(Translator translator);

172

173

/**

174

* Removes a translation source

175

* @param translator the translator to remove

176

*/

177

void removeSource(Translator translator);

178

179

/**

180

* Gets the default global translator instance

181

* @return the global translator

182

*/

183

static GlobalTranslator translator();

184

}

185

```

186

187

### Translation Registry

188

189

Registry interface for managing translations with locale support.

190

191

```java { .api }

192

/**

193

* Registry for translation keys and their translations

194

*/

195

interface TranslationRegistry extends Translator {

196

/**

197

* Registers a translation

198

* @param key the translation key

199

* @param locale the locale

200

* @param format the message format

201

*/

202

void register(String key, Locale locale, MessageFormat format);

203

204

/**

205

* Registers a simple string translation

206

* @param key the translation key

207

* @param locale the locale

208

* @param message the message string

209

*/

210

void register(String key, Locale locale, String message);

211

212

/**

213

* Unregisters a translation

214

* @param key the translation key

215

*/

216

void unregister(String key);

217

218

/**

219

* Gets all registered keys

220

* @return set of translation keys

221

*/

222

Set<String> keys();

223

224

/**

225

* Creates a new translation registry

226

* @return new registry

227

*/

228

static TranslationRegistry create();

229

}

230

```

231

232

### Translation Store

233

234

Storage system for translations with hierarchy support.

235

236

```java { .api }

237

/**

238

* Storage system for translations

239

*/

240

interface TranslationStore extends Translator {

241

/**

242

* Gets all available locales

243

* @return set of locales

244

*/

245

Set<Locale> locales();

246

247

/**

248

* Gets all translation keys

249

* @return set of keys

250

*/

251

Set<String> keys();

252

253

/**

254

* Checks if a translation exists

255

* @param key the translation key

256

* @param locale the locale

257

* @return true if translation exists

258

*/

259

boolean contains(String key, Locale locale);

260

}

261

262

/**

263

* Abstract base for translation storage implementations

264

*/

265

abstract class AbstractTranslationStore implements TranslationStore {

266

/**

267

* Template method for loading translations

268

* @param key the translation key

269

* @param locale the locale

270

* @return the message format or null

271

*/

272

protected abstract @Nullable MessageFormat loadTranslation(String key, Locale locale);

273

}

274

```

275

276

**Usage Examples:**

277

278

```java

279

import net.kyori.adventure.text.Component;

280

import net.kyori.adventure.translation.*;

281

import java.util.Locale;

282

import java.text.MessageFormat;

283

284

// Basic translatable component

285

Component welcome = Component.translatable("welcome.message");

286

287

// Translatable with arguments

288

Component playerJoined = Component.translatable("player.joined",

289

Component.text("PlayerName").color(NamedTextColor.YELLOW));

290

291

// With fallback for missing translations

292

Component customMessage = Component.translatable()

293

.key("custom.greeting")

294

.fallback("Hello, {0}!")

295

.arguments(TranslationArgument.string("World"))

296

.build();

297

298

// Register translations globally

299

GlobalTranslator global = GlobalTranslator.translator();

300

TranslationRegistry registry = TranslationRegistry.create();

301

302

registry.register("welcome.message", Locale.ENGLISH, "Welcome to the server!");

303

registry.register("welcome.message", Locale.FRENCH, "Bienvenue sur le serveur!");

304

registry.register("player.joined", Locale.ENGLISH, "{0} joined the game");

305

registry.register("player.joined", Locale.FRENCH, "{0} a rejoint la partie");

306

307

global.addSource(registry);

308

```

309

310

## Translation Patterns

311

312

### Resource Bundle Integration

313

314

```java

315

public class ResourceBundleTranslations {

316

public static void loadTranslations(String baseName) {

317

GlobalTranslator global = GlobalTranslator.translator();

318

319

// Load translations for different locales

320

for (Locale locale : getSupportedLocales()) {

321

try {

322

ResourceBundle bundle = ResourceBundle.getBundle(baseName, locale);

323

Translator translator = Translator.translator(bundle);

324

global.addSource(translator);

325

} catch (MissingResourceException e) {

326

// Handle missing resource bundle

327

logger.warn("Missing resource bundle for locale: " + locale);

328

}

329

}

330

}

331

332

private static Set<Locale> getSupportedLocales() {

333

return Set.of(

334

Locale.ENGLISH,

335

Locale.FRENCH,

336

Locale.GERMAN,

337

Locale.SPANISH,

338

new Locale("es", "MX") // Mexican Spanish

339

);

340

}

341

}

342

```

343

344

### Dynamic Translation Management

345

346

```java

347

public class DynamicTranslations {

348

private final TranslationRegistry registry = TranslationRegistry.create();

349

350

public void addTranslation(String key, Locale locale, String message) {

351

registry.register(key, locale, message);

352

}

353

354

public void addFormattedTranslation(String key, Locale locale, String pattern) {

355

MessageFormat format = new MessageFormat(pattern, locale);

356

registry.register(key, locale, format);

357

}

358

359

public void loadFromProperties(String propertyFile, Locale locale) {

360

Properties props = loadProperties(propertyFile);

361

for (String key : props.stringPropertyNames()) {

362

registry.register(key, locale, props.getProperty(key));

363

}

364

}

365

366

public Component createTranslatable(String key, Object... args) {

367

TranslationArgument[] arguments = Arrays.stream(args)

368

.map(this::toTranslationArgument)

369

.toArray(TranslationArgument[]::new);

370

371

return Component.translatable(key, arguments);

372

}

373

374

private TranslationArgument toTranslationArgument(Object obj) {

375

if (obj instanceof Component) return TranslationArgument.component((Component) obj);

376

if (obj instanceof Number) return TranslationArgument.numeric((Number) obj);

377

if (obj instanceof Boolean) return TranslationArgument.bool((Boolean) obj);

378

return TranslationArgument.string(String.valueOf(obj));

379

}

380

}

381

```

382

383

### Locale-Aware Messaging

384

385

```java

386

public class LocalizedMessaging {

387

private final GlobalTranslator translator = GlobalTranslator.translator();

388

389

public void sendLocalizedMessage(Audience audience, String key, Object... args) {

390

// Get player's locale (implementation-specific)

391

Locale playerLocale = getPlayerLocale(audience);

392

393

// Create translatable component

394

Component message = Component.translatable(key,

395

Arrays.stream(args)

396

.map(TranslationArgument::string)

397

.toArray(TranslationArgument[]::new));

398

399

// Send with locale context

400

audience.sendMessage(message);

401

}

402

403

public void broadcastLocalized(Collection<? extends Audience> audiences, String key, Object... args) {

404

Component message = Component.translatable(key,

405

Arrays.stream(args)

406

.map(TranslationArgument::string)

407

.toArray(TranslationArgument[]::new));

408

409

for (Audience audience : audiences) {

410

audience.sendMessage(message);

411

}

412

}

413

414

public Component formatNumber(Number number, Locale locale) {

415

NumberFormat formatter = NumberFormat.getInstance(locale);

416

return Component.text(formatter.format(number));

417

}

418

419

public Component formatTime(long timestamp, Locale locale) {

420

DateFormat formatter = DateFormat.getDateTimeInstance(

421

DateFormat.MEDIUM, DateFormat.MEDIUM, locale);

422

return Component.text(formatter.format(new Date(timestamp)));

423

}

424

}

425

```

426

427

### Translation Validation

428

429

```java

430

public class TranslationValidator {

431

public void validateTranslations(TranslationStore store) {

432

Set<String> keys = store.keys();

433

Set<Locale> locales = store.locales();

434

435

for (String key : keys) {

436

for (Locale locale : locales) {

437

if (!store.contains(key, locale)) {

438

logger.warn("Missing translation: {} for locale: {}", key, locale);

439

}

440

441

// Validate message format

442

MessageFormat format = store.translate(key, locale);

443

if (format != null) {

444

validateMessageFormat(key, locale, format);

445

}

446

}

447

}

448

}

449

450

private void validateMessageFormat(String key, Locale locale, MessageFormat format) {

451

try {

452

// Test with sample arguments

453

format.format(new Object[]{"test", 123, true});

454

} catch (IllegalArgumentException e) {

455

logger.error("Invalid message format for {}: {} ({})", key, locale, e.getMessage());

456

}

457

}

458

459

public Set<String> findMissingTranslations(Set<String> requiredKeys, TranslationStore store) {

460

return requiredKeys.stream()

461

.filter(key -> store.locales().stream()

462

.noneMatch(locale -> store.contains(key, locale)))

463

.collect(Collectors.toSet());

464

}

465

}

466

```

467

468

## Best Practices

469

470

### Translation Key Conventions

471

- Use hierarchical keys: `menu.main.title`, `error.permission.denied`

472

- Keep keys descriptive and consistent

473

- Use namespaces for different modules: `plugin.feature.message`

474

475

### Argument Handling

476

- Use typed arguments (numbers, booleans) when possible

477

- Provide meaningful argument names in translation files

478

- Consider argument order differences between languages

479

480

### Fallback Strategies

481

- Always provide fallback text for custom keys

482

- Use English as the base language for fallbacks

483

- Handle missing translations gracefully

484

485

### Performance Considerations

486

- Cache frequently used translations

487

- Load translations at startup, not runtime

488

- Use global translator efficiently to avoid duplicate lookups