I18n
Poetry splits translation duty cleanly: the library's own interface strings ride I18n keys you can override per locale, and every string that is your content arrives as an option you pass - copy lives in your templates and locale files, never hardcoded in a component.
The shipped catalogue
The built-in interface strings - accessible names like Close and
Clear selection, empty and loading states, screen-reader counts -
resolve through I18n.t under the poetry
namespace, one family per key group:
poetry.<component>.<key>. The catalogue
ships in poetry-ui's config/locales/en.yml and loads
through the standard engine convention; that file is the complete
poetry.* key list, and pluralization and %{interpolation}
follow I18n's own rules.
en:
poetry:
combobox:
clear: "Clear selection"
empty: "No results found."
remove: "Remove %{label}"
command:
results:
zero: "0 results"
one: "1 result"
other: "%{count} results"
dialog:
close: "Close"
deferred:
failed: "This section failed to load."
retry: "Retry"
toast:
region_label: "Notifications (%{hotkey})"
Overriding keys and adding locales
Your application's locale files load after the engine's, so
redefining a key overrides it - per key, not per file. The same
mechanism adds a language: ship the poetry.* keys for
the new locale, and enable fallbacks so a key you have not
translated yet reads the shipped English instead of rendering a
translation-missing marker.
# config/locales/poetry.es.yml (your app's file loads after the
# engine's, so a key you redefine wins - per key, not per file)
es:
poetry:
combobox:
clear: "Quitar selección"
empty: "Sin resultados."
dialog:
close: "Cerrar"
# config/application.rb
config.i18n.available_locales = [:en, :es]
config.i18n.fallbacks = [:en] # a poetry.* key you have not translated yet reads English
Visible copy is yours
Strings that carry meaning in your product - labels, placeholders,
empty-state prose, filter captions - are options, not library
keys: label:, placeholder:,
empty_text:, filter_label:. Their
defaults are English conveniences for a
zero-config start (DatePicker's "Pick a date"); a
localized app passes its own translations at the call site, with
Rails' usual template-scoped t(".key") lookup.
poetry_data_table(rows: @orders,
empty_text: t(".no_orders"),
filter_label: t(".filter_orders"))
poetry_date_picker(name: "due_on", placeholder: t(".pick_due_date"))
Components you author on Poetry::Core::Component
speak relative lookup too: t(".title") resolves under
a scope derived from the class name
(admin.alert_component.title), and assigning
ViewComponent's own attribute in the class -
self.virtual_path = "view_components/admin/alert" -
reshapes that scope when you want a designed keyspace.
Forms localize through the model
The FormBuilder derives its labels
from human_attribute_name, so they localize through
the activerecord.attributes keys you already
maintain; validation messages arrive through Active Model's own
I18n chain. Hints and placeholders resolve from locale files too:
poetry_form.hints.<model>.<attribute>
(then a defaults rung), falling back to the same
keys under simple_form.* - a migrating app's
existing locale files keep working - with hint: and
placeholder: arguments overriding both.
# config/locales/es.yml
es:
activerecord:
attributes:
order:
due_on: "Fecha de entrega"
poetry_form:
hints:
order:
due_on: "Formato: AAAA-MM-DD"
# Label and hint localize with zero view changes:
# form.field(:due_on) # label via human_attribute_name, hint via poetry_form.hints
Dates
DateField and TimeField speak the catalogue: segment names and
placeholders (year, yyyy,
AM/PM) live under poetry.date_field.*,
so a locale file retitles every segment. Calendar follows your
locale files end to end: the weekday header derives its
two-letter labels from Rails' date.abbr_day_names,
the caption and the month dropdown read
date.month_names - the localized names also ride a
Stimulus value, so client-side month navigation stays localized
with no extra wiring - and the dropdown's screen-reader labels
live at poetry.calendar.*.
The accessible names this page's catalogue ships are part of the accessibility contract - the keyboard and screen-reader protocol for verifying them in your app lives in the accessibility guide.