Skip to content

UI and localization groundwork before domain work

Before the first real domain functionality gets added to the tournament app, there’s one last infrastructure and setup step.

Again, no player CRUD.

Again, no tournament list.

Again, no group assignment.

Instead:

Transloco
Tailwind
PrimeNG

At first, that sounds like technical background noise. Like project setup. Like things you could just as easily add later.

You could.

But experience suggests that’s exactly the point: you can add it later, but you often pay for it with unnecessary friction.

After the previous steps, the technical foundation is in place.

The application runs. The containers are deployed. The API gateway is secured. Keycloak is integrated. CI’s static quality gates pass.

That would make it tempting to jump straight into domain work now.

Still, it’s worth settling the UI and localization foundation first.

Because as soon as features get built, so do text, buttons, tables, forms, dialogs, validation messages, and layout decisions.

If these things grow without a shared foundation, you see it immediately later:

  • text is hardcoded
  • components look inconsistent
  • spacing happens at random
  • forms get rebuilt from scratch every time
  • translation gets retrofitted into existing templates afterward
  • UI conventions emerge by accident instead of by decision

So this step isn’t glamorous. But it reduces later friction.

Transloco, even though it’s only German for now?

Section titled “Transloco, even though it’s only German for now?”

For the first version of the tournament app, German is entirely sufficient.

The club is local. The users are German-speaking. The first features get built for trainers, referees, and possibly parents. So there’s no immediate product pressure for multiple languages.

Still, Transloco goes into the project early.

Not because the app has to go international right away. But because text in an application very quickly ends up everywhere.

A button here. An error message there. A table header. An empty state. A dialog description. A toast. A menu title.

If you hardcode this text everywhere at first, internationalization has to be retrofitted later, and that’s no longer a small change. Then you have to go through templates, components, tests, and validations and retroactively build structure.

That’s exactly the kind of work nobody enjoys.

So i18n gets prepared even though only one language currently exists.

de

That’s enough.

The value at the start isn’t the second language. The value is that text has a defined home from the very beginning.

Internationalization often gets treated as a translation topic.

For me, at this step, it’s more of a structural decision.

Instead of scattering text directly across components, the application gets a clear rule:

UI text lives in translation files, not scattered randomly through the template.

That’s especially helpful once coding agents start adding features later.

An agent building a form shouldn’t start freely scattering text through the HTML. It should use the existing i18n conventions.

That makes Transloco not just a localization tool. It’s also a guardrail against UI drift.

A later prompt can then say:

Use the existing Transloco structure.
No hardcoded UI text.
Add new keys cleanly, in the appropriate namespace.

That’s much better than cleaning it up later.

Tailwind comes in as a layout and utility layer.

Not because every component from now on should consist of twenty utility classes. And not because CSS files are forbidden.

Tailwind is mostly practical here for:

  • spacing
  • layouts
  • flex/grid structures
  • responsive adjustments
  • simple visual composition
  • small UI fixes without inventing new custom classes

Especially in an application that probably needs to work on tablet or laptop on tournament day, fast and consistent layout matters.

Tournament setup, group assignment, result recording, and standings views will have different layout requirements. Tailwind helps build these layouts pragmatically, without having to invent a new CSS class for every little thing.

Still, the rule stands:

Tailwind is a tool, not a free pass for visual chaos.

Conventions are still needed here too. Otherwise, utility-first quickly turns into utility soup.

PrimeNG is the choice for the UI components.

This isn’t a rejection of Angular Material in principle.

On the contrary: Angular Material is strong. It’s very well integrated into Angular, reliable, well documented, and the obvious choice in a lot of projects. In professional Angular projects, Material is often the more cost-effective choice, because it fits naturally with Angular.

If a team is using Angular anyway, Material is rarely the wrong call.

So why PrimeNG?

The honest answer is: it’s partly a matter of taste.

PrimeNG offers a broad component palette and feels very complete for a lot of business-style interfaces. Tables, dialogs, dropdowns, buttons, toasts, panels, and plenty of other components are readily available. For a small administration and tournament app, that’s attractive.

There’s also a personal reason: professionally, I work almost exclusively with Angular Material.

That’s not a problem. But this project is also an architecture lab. It’s not just meant to solve a club’s problem — it’s also meant to create room for experience.

So privately, it’s allowed to be something different for once.

Important: this isn’t Material-versus-PrimeNG bashing.

Either option would be defensible.

Angular Material would probably be the more conservative choice. PrimeNG is the deliberately chosen alternative here.

So the decision isn’t:

Material is bad.
PrimeNG is right.

But more like:

Material would be completely viable.
PrimeNG fits well enough for this project
and also gives me additional learning value.

That’s a legitimate architecture decision.

Not every technical decision has to be derived mathematically from ten objective criteria. Sometimes experience, motivation, and learning value play a role too.

As long as the decision gets made deliberately, that’s fine.

The setup step has a clear goal:

The application should be ready for domain features
with a consistent UI and text foundation.

Concretely, that means:

  • Transloco is set up
  • German is prepared as the first language
  • UI text runs through translation keys
  • Tailwind is integrated
  • PrimeNG is configured
  • theme and styles are basically in place
  • an initial shell/placeholder UI uses the new foundation
  • builds and CI stay green

Again, this isn’t a feature in the domain sense.

But it prevents every later feature from bringing its own UI foundation along.

This step stays bounded too.

Not included:

  • no complete design language for the whole app
  • no fully worked-out tournament-day UX
  • no player management
  • no group assignment
  • no result dialogs
  • no complex table views
  • no second language
  • no complete component library

The goal isn’t to build the final interface.

The goal is to provide the tools cleanly.

For AI-assisted development, this step is especially valuable.

Coding agents orient strongly on existing patterns. If no UI convention exists yet, they invent their own. And if several prompts run one after another without a clear UI foundation, different styles emerge quickly.

One feature uses hardcoded text.

The next feature creates its own CSS classes.

A third feature uses PrimeNG components differently.

A fourth feature builds its own buttons.

That’s not malicious. It’s expected.

That’s why the foundation should be visible early.

The agent needs examples and boundaries:

Use PrimeNG for standard components.
Use Tailwind for layout.
Use Transloco for UI text.
No hardcoded labels.
No custom button component without a reason.

The more clearly these rules are reflected in the project, the less the agent has to guess.

After this setup, it’s not enough to just check whether the app still starts.

Important review questions:

  • Is Transloco cleanly integrated into the standalone app?
  • Is there a clear structure for German translations?
  • Are new UI texts already wired up through translation keys?
  • Is Tailwind correctly integrated?
  • Does Tailwind unintentionally affect PrimeNG styling?
  • Is PrimeNG cleanly configured?
  • Is there an initial theme or a traceable styling foundation?
  • Does the app structure stay clean?
  • Were no domain features quietly added along the way?
  • Do build, format, and CI stay green?

That last point matters especially: setup steps must not make the project unstable. They’re supposed to reduce friction, not create new friction.

With this step, the infrastructure journey is done for now.

The tournament app now has:

Frontend app
API gateway
Backend service
Database
RabbitMQ
Keycloak login
JWT protection in the API gateway
Stable user ID via sub
Audience check
Transloco
Tailwind
PrimeNG
CI quality gates
Deployment

That’s a lot of foundation for a project that doesn’t do much on the domain side yet.

But that’s exactly the point.

Domain functionality shouldn’t get built on sand. It should get built on a project that can already start, build, deploy, authenticate, and consistently display UI.

This step isn’t spectacular.

But it’s the last necessary setup step before the actual tournament domain work can meaningfully begin.

Transloco makes sure UI text is structured from the start.

Tailwind gives the app a flexible layout foundation.

PrimeNG delivers a broad component base for the upcoming administration and tournament interfaces.

And the decision for PrimeNG is deliberately pragmatic: Angular Material would have been entirely possible. But for this private architecture lab, it’s allowed to be a different tool too — not out of rebellion, but out of curiosity and taste.

With that, the stage is set.

From here on, the actual domain work can begin.