Class I18nString
String at the point of use.
A component stores one of these where it would otherwise store a
String, and reads get() when it measures or paints. Holding the
value is what makes keeping it correct: a lookup call whose result must never be
kept in a field fails silently, leaving one widget in yesterday's language.
private static final I18nString PLACEHOLDER =
new I18nString("limn.searchField.placeholder", "Search…");
...
canvas.drawText(PLACEHOLDER.get(), x, baseline, font, colour);
English is not a translation. The second argument is both the default text and the fallback for every key a bundle does not answer, so an application that registers nothing behaves exactly as it did before this type existed, and a missing translation degrades to English rather than to a key name on screen.
The cache lives here. get() memoises its resolution against
I18n.epoch(), so nothing subscribes and nothing leaks: the re-resolution
rides the relayout a language change already causes.
Declare them static final, so the cache is one slot per key for the whole
process rather than one per widget; a list showing five hundred rows of the same
label resolves once per language change, not five hundred times.
Immutable in every respect a caller can observe; the memo is mutated on the UI thread like the rest of the toolkit's state.
-
Field Summary
FieldsModifier and TypeFieldDescriptionstatic final I18nStringThe empty literal: a text field's placeholder before anyone sets one. -
Constructor Summary
Constructors -
Method Summary
Modifier and TypeMethodDescriptionenglish()The default-language text: also the fallback, and nevernull.booleanValue equality on key and English: two literals of the same text are the same string, and a component's "did this actually change" check keeps working when its field stops being aString.The text with{0}-style arguments substituted, formatted byMessageFormatunder the current locale.get()The text in the locale in effect here, or the English when nothing translates it.inthashCode()booleanWhether this is a literal, which no bundle can ever change.@Nullable Stringkey()The lookup key, ornullfor a literal.static I18nStringText that is not localizable and resolves to itself: whatsetText(String)wraps a caller's literal in, so a component can hold one field instead of two and never has to decide which of them wins.toString()The resolved text, so logging and debugging read as the user sees it.withArguments(@Nullable Object @Nullable ... args) This text with{0}-style arguments, as a text that follows the language: whereformat(java.lang.Object...)answers aStringin the language of the moment, which a label given it keeps after the language changes, this answers anI18nStringthat formats again in the language in effect wherever and whenever it is read.This text as words, as the toolkit shows it with its access letter's mark taken out: for a string a menu or a button shows with its letter, quoted in a sentence, a status line or a log.
-
Field Details
-
EMPTY
The empty literal: a text field's placeholder before anyone sets one.
-
-
Constructor Details
-
I18nString
Declares a localizable string.- Parameters:
key- the lookup key, dotted and globally unique; prefix it with the screen or component that owns it (settings.title, nottitle), because bundles share one namespaceenglish- the text in the default language, used whenever no registered bundle answers the key- Throws:
IllegalStateException- if the key was already declared with different English: one key with two meanings is a bug that would otherwise surface as a mistranslation
-
-
Method Details
-
literal
Text that is not localizable and resolves to itself: whatsetText(String)wraps a caller's literal in, so a component can hold one field instead of two and never has to decide which of them wins. -
get
The text in the locale in effect here, or the English when nothing translates it. Inside a widget's measure, layout, paint or event dispatch that locale is the widget's own effective one, so the same declaration reads correctly from every subtree without the caller doing anything. -
withoutAccessLetter
This text as words, as the toolkit shows it with its access letter's mark taken out: for a string a menu or a button shows with its letter, quoted in a sentence, a status line or a log. "&Open" reads "Open", "R&&D" reads "R&D", an&before anything but a letter or a digit is kept ("Terms & conditions"), and the parenthesised letter Japanese, Korean and Chinese strings carry goes whole, on every system, with an ellipsis after it kept: "名前(&N)" reads "名前" and "プロパティ(&P)…" reads "プロパティ…". The reading is the controls' own, so the words are the ones a control shows.Resolved at
get(), like this string: it follows a change of language, and the mark is taken out of whichever translation is in effect, each of which picks its own letter.format(java.lang.Object...)substitutes its arguments into the words, and an argument's own ampersands are left alone. Called on its own result, it returns that result.- Returns:
- the words;
thiswhen it is already words
-
format
The text with{0}-style arguments substituted, formatted byMessageFormatunder the current locale. Never cached: the arguments vary per call, and caching them would be caching the wrong thing.Only parameterized keys pay MessageFormat's rules, which is why
get()does not route through it: an apostrophe in an ordinary string needs no escaping. In a parameterized one it does (double it), and so does a stray brace; a pattern a translator got wrong falls back to the English rather than throwing in the middle of a paint.A number argument is written the way
NumberFormatswrites one: in the digits the toolkit shows for the language, with the separators, signs and grouping that go with those digits, and so are the digits of a{0,date}or{0,time}argument. The platform's own idea of a language's digits is not always the toolkit's: it writes Arabic-Indic digits for a barear, where every other number on the screen is Latin, and a sentence formatted under the bare locale carried its count in the digits nothing beside it used. Everything else the locale decides, the words of a date and a currency's symbol among them, is the language's as before. PassInteger.toString(n)where a number is a spelling rather than a quantity and must stay in ASCII digits. -
withArguments
This text with{0}-style arguments, as a text that follows the language: whereformat(java.lang.Object...)answers aStringin the language of the moment, which a label given it keeps after the language changes, this answers anI18nStringthat formats again in the language in effect wherever and whenever it is read. Give it to anything that takes anI18nString(Label.setText, a button, an accessible name) and a change of language reaches it as it reaches a plain declared string.An argument that is itself an
I18nStringis read in that language too; numbers are written asformat(java.lang.Object...)writes them. Two texts made from the same string and equal arguments are equal, so setting the same sentence again is no change.The arguments are values, taken at the call. What follows is the language, not the arguments: the sentence is formatted once in each language it is read in and kept, as a declared string's translation is, so a count or a name that changes afterwards does not reach a text already made. Make a new one with the new value and set it again, as a label showing a count does each time the count moves. The array is copied; an object in it that changes after the call is read again only when the language does, and by
equals(java.lang.Object)when two texts are compared, so pass what it holds (builder.toString(), a copy of a date) rather than the object.- Parameters:
args- the arguments, in their positions- Returns:
- the text with its arguments
-
key
The lookup key, ornullfor a literal. -
english
The default-language text: also the fallback, and nevernull. ForwithoutAccessLetter(), its words. -
isLiteral
public boolean isLiteral()Whether this is a literal, which no bundle can ever change. -
toString
The resolved text, so logging and debugging read as the user sees it. -
equals
Value equality on key and English: two literals of the same text are the same string, and a component's "did this actually change" check keeps working when its field stops being aString. A string without its access letter equals only another such of the same key and English, since a translation can mark a letter the English does not. -
hashCode
public int hashCode()
-