Localizing
Packages can support different languages. WebWriter can then display widgets, snippets and package metadata in a localized form.
Localizing Lit packages automatically (recommended)
WebWriter’s build tool @webwriter/build
and the preconfigured base class LitElementWw
from @webwriter/lit
comes with support for @lit/localize
. This is especially useful if you are dealing with sub-components in your widgets, or need to translate expressions that include variables.
Step 0 (Optional): Create a free DeepL account with an API key
Using the DeepL API, we can machine translate our whole widget! While this is optional, it is highly recommended as a starting point. For this, you need a DeepL API Key:
- Create a Free (or Pro) account at https://deepl.com
- Head to Your account and create a new API key, then copy it. Steps can be found here.
- Create a file
.env
in your project root and add the entryDEEPL_API_KEY=...
. Caution: Make sure.env
is in your.gitignore
so your secret key isn’t published accidentally.
Step 1: Make package content localizable
For @webwriter/build
to be able to localize the widgets, snippets, and metadata of the package, some extra steps are needed.
Make widgets localizable
LitElementWw
can automatically trigger an update when the lang
attribute of your widget changes, causing the widget to render with the new language. For this to work, you need to mark all translatable strings in your code with the msg
function. This can be used anywhere a normal string would be used, including in templates for text or attributes.
We add some boilerplate code (see @lit/localize documentation for details). This boilerplate is included in the template and can be uncommented. It allows your widget to notify @lit/localize
that the widget lang
has changed, causing it to load different translations and to re-render the widget accordingly.
import {html} from "lit"
import {LitElementWw} from "@webwriter/lit"
import {customElement, property} from "lit/decorators.js"
import LOCALIZE from "../../localization/generated"
import {msg} from "@lit/localize"
@customElement("cool-widget")
export class CoolWidget extends LitElementWw {
localize = LOCALIZE
render() {
return html`<span title=${msg("This is a greeting")}>${msg("Hello, world!")}</span>`
}
}
Refer to the @lit/localize
documentation for more infos.
Make snippets localizable
No further steps are needed for snippets to be translatable: All text nodes in the snippet’s HTML structure are considered for translation and translations are embedded as a <script>
tag into the snippet itself later.
Make package metadata localizable
The editing config may contain localizable metadata for the package itself and package members (label
, description
). If such metadata is provided in English (using the _
or en
key), it will be considered for localization. A special case is the standard description
property, which is always considered for localization.
{
"description": "This package has several cool features..."
// ...
"editingConfig": {
".": {
"label": {
"_": "COOL Package"
}
}
}
}
Step 2: Localize package members
Open your terminal and run npx @webwriter/build localize
. This should create a new subfolder localization
containing an exchange file for each widget language (e.g. de.xlf
for German). For snippets and package metadata, similar subfolders (localization/snippets
, localization/pkg
) are created. Also a folder localization/generated
should have been generated - we expect to import that folder in step 1.
If you configured a DeepL API key in step 0, you are asked whether you want to machine translate the package members. Note that this consumes tokens and may incur costs if you use a Pro account (Free accounts may run into the token limit instead).
Finally, translations from the exchange files are applied to the widgets (as @lit/localize
JSON files), snippets (inlined script with JSON), and metadata (external editing config file).
Note:
- You can run
npx @webwriter/build localize
again to localize your package after adding more messages to translate. - No messages are overwritten (neither by the build tool nor by DeepL). This means you can manually change the translation in the
.xlf
/.xliff
files - for example to correct faulty machine translations. If you make a correction, you can rerunnpx @webwriter/build localize
to apply your changes.
Step 3: Rebuild with localization (widgets only)
For the widgets to update with the new localization, they need to be rebuilt. Changes in the snippets and package metadata should be detected automatically.
Localizing manually with lang
WebWriter passes the configured language to each widget using the Web Standard lang
attribute. Widgets should change the displayed language based on that attribute.
Consider this example for a widget only providing a German translation:
import {html} from "lit"
import {LitElementWw} from "@webwriter/lit"
import {customElement, property} from "lit/decorators.js"
@customElement("cool-widget")
export default class CoolWidget extends LitElementWw {
static LOCALIZED = {
"What's your name?": {"de": "Wie heißt du?"}
}
// Helper function: Get localized form of `str` if available, otherwise fall back to `str`
msg = (str: string) => CoolWidget.LOCALIZED[str]?.[this.lang] ?? str
@property({attribute: true})
accessor value: string
render() {
return html`
<span>${this.msg("What's your name?")}</span>
<textarea @change=${e => this.value = e.target.value}>
${this.value}
</textarea>`
}
}