Layouts
Page layouts wrap content with common structure (nav, footer).
Basic Layout
public class Layout implements Template {
private final String title;
private final Element content;
public Layout(String title, Element content) {
this.title = title;
this.content = content;
}
public Element render() {
return html(
head(title(title)),
body(new Nav(), main(content), new Footer())
);
}
}Register Layout
// In Routes.java - set default layout
app.layout(Layout.class);
// Pages automatically wrapped
app.pages(
"/", HomePage.class,
"/about", AboutPage.class
);Theme Tokens
public final class Theme {
// Colors
public static final CSSValue PRIMARY = hex("#6366f1");
public static final CSSValue TEXT = hex("#1e293b");
// Spacing
public static final CSSValue SP_4 = rem(1);
public static final CSSValue SP_8 = rem(2);
}
// Usage
div(attrs().style().color(PRIMARY).padding(SP_4).done())Define design tokens in Theme.java for consistent styling across your app.
Internationalization (i18n)
Build multilingual applications with translated messages.
import com.osmig.Jweb.framework.i18n.I18n;
// Get translated message
String title = I18n.t("page.title");
// With parameters
String greeting = I18n.t("welcome", username);
// "welcome" = "Hello, {0}!" → "Hello, John!"Message Files
Create message files in src/main/resources/messages/:
// messages_en.properties
page.title=Welcome
nav.home=Home
nav.about=About
welcome=Hello, {0}!
errors.required=This field is required
// messages_es.properties
page.title=Bienvenido
nav.home=Inicio
nav.about=Acerca de
welcome=¡Hola, {0}!
errors.required=Este campo es obligatorioIn Templates
public class HomePage implements Template {
public Element render() {
return div(
h1(I18n.t("page.title")),
nav(
a(href("/"), I18n.t("nav.home")),
a(href("/about"), I18n.t("nav.about"))
)
);
}
}Locale Detection
I18n detects locale from (in order):
// 1. Query parameter: /page?lang=es
// 2. Session attribute: lang
// 3. Cookie: lang
// 4. Accept-Language header
// 5. Default locale
// Get current locale
Locale locale = I18n.getLocale(req);
// Use middleware for automatic detection
app.use(I18n.middleware());Language Picker
// Get supported locales
List<I18n.LocaleInfo> locales = I18n.getSupportedLocales();
// Each has: code, nativeName, englishName
// In template
form(method("post"), action("/language"),
select(name("lang"), onChange("this.form.submit()"),
each(locales, locale ->
option(
value(locale.code()),
selected(locale.code().equals(I18n.current().getLanguage())),
text(locale.nativeName()) // "Español" for Spanish
)
)
)
)Switch Language
// Via session
app.post("/language", req -> {
String lang = req.formParam("lang");
req.session().setAttribute("lang", lang);
return Response.redirect(req.header("Referer"));
});
// Via cookie (persistent)
Cookie.of("lang", lang)
.maxAge(Duration.ofDays(365))
.addTo(response);Use nativeName() in language pickers - users recognize their language better.