<bmx-translation-editor>
A localisation workbench: open a translation file - XLIFF 1.2 or 2.x,
gettext PO or POT, an Apple String Catalog - or a one-language source file
with its translation (Android strings.xml, Apple .strings, JSON message
files, Flutter ARB, .NET .resx), translate string by string with source
and translation side by side, and save the file back in its own format with
only the translated strings changed.
32 properties · 4 events · 17 methods · 12 parts
Example
Try it with your own files in confidence: they are opened by your browser, on your device. Nothing is uploaded, no copy reaches us or anyone else, and nothing is kept once you close this tab.
Reading...
Show markup
<bmx-translation-editor id="ex-tr" label="Polish translation" description="A gettext file part way through: one string needs review, one has lost a placeholder, and Polish needs three plural forms where English has two. The memory and glossary below are loaded too."></bmx-translation-editor>
<p class="note" id="ex-tr-out" role="status" style="margin: 12px 0 0">Reading...</p>
<script type="module">
await customElements.whenDefined('bmx-translation-editor');
const editor = document.getElementById('ex-tr');
const out = document.getElementById('ex-tr-out');
// A made-up booking app's Polish catalogue.
const po = `# Polish translation of the Harbour Lane booking app.
msgid ""
msgstr ""
"Project-Id-Version: harbour-lane 3.2\\n"
"Language: pl\\n"
"MIME-Version: 1.0\\n"
"Content-Type: text/plain; charset=UTF-8\\n"
"Content-Transfer-Encoding: 8bit\\n"
"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 "
"|| n%100>=20) ? 1 : 2);\\n"
#. Heading on the bookings page
#: src/bookings/list.html:4
msgid "Your bookings"
msgstr "Twoje rezerwacje"
#: src/bookings/list.html:12
#, c-format
msgid "%d booking this week"
msgid_plural "%d bookings this week"
msgstr[0] "%d rezerwacja w tym tygodniu"
msgstr[1] ""
msgstr[2] ""
#. %s is the guest's name
#: src/bookings/card.html:8
#, c-format
msgid "Booked by %s on %s"
msgstr "Zarezerwowane przez %s"
#: src/bookings/card.html:21
#, fuzzy
#| msgid "Cancel the booking"
msgid "Cancel this booking"
msgstr "Anuluj rezerwację"
#: src/bookings/card.html:22
msgid "Change the booking"
msgstr ""
#: src/account/menu.html:3
msgid "Account settings"
msgstr ""
#: src/account/menu.html:9
msgid "Sign out"
msgstr "Wyloguj się"
#. Shown after a booking is paid for
#: src/pay/done.html:2
msgid "Thank you! Your booking is confirmed."
msgstr ""
`;
// Earlier translations from the team's memory, and its glossary.
const tmx = `<?xml version="1.0" encoding="UTF-8"?>
<tmx version="1.4">
<header creationtool="example" creationtoolversion="1" segtype="sentence" o-tmf="example" adminlang="en" srclang="en" datatype="plaintext"/>
<body>
<tu><tuv xml:lang="en"><seg>Change the booking details</seg></tuv><tuv xml:lang="pl"><seg>Zmień szczegóły rezerwacji</seg></tuv></tu>
<tu><tuv xml:lang="en"><seg>Thank you! Your order is confirmed.</seg></tuv><tuv xml:lang="pl"><seg>Dziękujemy! Twoje zamówienie zostało potwierdzone.</seg></tuv></tu>
<tu><tuv xml:lang="en"><seg>Account settings</seg></tuv><tuv xml:lang="pl"><seg>Ustawienia konta</seg></tuv></tu>
</body>
</tmx>`;
const glossary = `en,pl,forbidden
booking,rezerwacja,
booking,booking,yes
account,konto,`;
editor.glossary = glossary;
await editor.addMemory(tmx, 'team.tmx');
await editor.load({ target: { name: 'po/pl.po', text: po } });
const report = async () => {
const issues = (await editor.getIssues()).filter(i => i.severity === 'error');
const p = await editor.getProgress();
const done = p.byState.translated.units + p.byState.approved.units;
out.textContent = `${done} of ${p.units} strings translated; ${issues.length ? `${issues.length} error${issues.length === 1 ? '' : 's'} to fix` : 'no errors'}.`;
};
// The checks run a moment after each change.
editor.addEventListener('bmxUnitChange', () => setTimeout(report, 200));
await report();
</script>
<bmx-translation-editor label="French translation"></bmx-translation-editor>
<script>
editor.load({ target: { name: 'fr.po', text: poText } });
</script>
Inline markup - XLIFF tags, Android <b> and <xliff:g> - shows as
numbered tokens (⟨1⟩ ⟨/1⟩ ⟨2/⟩) that can be inserted but not broken;
placeholders (%s, %1$@, {name}, {{count}}) are marked; plural strings
get one field per plural form the target language has, labelled with the
numbers that take it; ICU messages are previewed with sample values in the
target language; right-to-left languages are edited right to left.
A translation memory (TMX files, plus every string confirmed in the session) offers earlier translations with a score and what differs, with changed numbers already put right; a glossary (TBX or CSV) marks its terms in the source and says how to translate them. Checks run as you type: placeholders and tags kept, ICU syntax and plural categories, empty forms, numbers, length limits, spacing, ending punctuation, glossary terms.
Nothing leaves the page: files are read in the browser, and saving
downloads the file (or, cancelling bmxSave, hands its text and bytes to the
page). In a form, name receives the translated file's text.
Keyboard in a translation: Ctrl+Enter confirms and moves to the next string to do, Alt+Up/Down moves to the previous or next string, Ctrl+Insert copies the source, F9 inserts the next tag or placeholder the translation lacks, Ctrl+Shift+1 to 9 use a memory match. In the list: arrows, Home/End, Page Up/Down, and Enter to edit.
Properties
| Property | Attribute | Type | Default | Description |
|---|---|---|---|---|
allowOpen |
allow-open |
boolean |
true |
Offer opening files (the Open button and dropping files). |
allowSave |
allow-save |
boolean |
true |
Offer saving (the Save button). |
description |
description |
string |
— | Help text below the editor. |
disabled |
disabled |
boolean |
false |
Disable the editor. |
disabledChecks |
disabled-checks |
BmxL10nCheck[] | string |
— | Checks to leave out, as an array, JSON or a comma list (numbers, punctuation). |
errorText |
error-text |
string |
— | An error supplied by the page. |
filter |
filter |
BmxTranslationFilter |
'all' |
Which strings the list shows. Two-way. |
format |
format |
BmxL10nFormat |
— | The format, when names and content do not say. |
glossary |
glossary |
string | BmxGlossary |
— | A glossary: TBX or CSV text, or one read with readGlossary. |
glossarySrc |
glossary-src |
string |
— | An address of a TBX or CSV glossary. |
hideLabel |
hide-label |
boolean |
false |
Hide the label visually while keeping it as the accessible name. |
hideToolbar |
hide-toolbar |
boolean |
false |
Hide the toolbar. |
label |
label |
string |
— | The field's label: names the whole control. |
memory |
memory |
string | BmxTmEntry[] |
— | A translation memory: TMX text, or entries. |
memorySrc |
memory-src |
string |
— | An address of a TMX file to load into the memory. |
minMatch |
min-match |
number |
70 |
The lowest memory match score shown (0-100). |
name |
name |
string |
— | The field's name in a form: it receives the translated file's text. |
propagate |
propagate |
boolean |
true |
On confirming a string, fill the empty strings with the same source with the same translation. |
readonly |
readonly |
boolean |
false |
Allow reading, searching and copying, but not changing. |
requireComplete |
require-complete |
boolean |
false |
Keep the form from submitting until every string is translated. |
requireValid |
require-valid |
boolean |
false |
Keep the form from submitting while a check finds an error. |
search |
search |
string |
'' |
Text to search for in source, translation and key. Two-way. |
source |
source |
string |
— | A one-language format's source-language file. |
sourceLanguage |
source-language |
string |
— | The source language, when the file does not say. |
sourceName |
source-name |
string |
— | The source file's name. |
sourceSrc |
source-src |
string |
— | An address to load a one-language format's source file from. |
src |
src |
string |
— | An address to load the translation file from (the page's own server). |
strings |
strings |
Partial<BmxTranslationEditorStrings> | string |
— | Words to show instead of the English ones, as an object or JSON. |
target |
target |
string |
— | The translation file's text: a bilingual file (XLIFF, PO, String Catalog), or a one-language format's translated file. |
targetLanguage |
target-language |
string |
— | The language translated into: a String Catalog's language, or a new one-language translation's. Two-way. |
targetName |
target-name |
string |
— | The translation file's name (it tells the format and language, and names the saved file). |
user |
user |
string |
— | Who is translating, kept with the memory entries made here. |
Events
| Event | Detail | Description |
|---|---|---|
bmxLoad |
BmxTranslationLoadDetail |
Fired after a file is read. |
bmxSave |
BmxTranslationSaveDetail |
Fired before the file is downloaded. Cancel it to save the text yourself. |
bmxSelect |
BmxTranslationSelectDetail |
Fired when another string is selected. |
bmxUnitChange |
BmxTranslationUnitDetail |
Fired after a string's translation or state changes. |
Methods
| Method | Signature | Description |
|---|---|---|
addMemory |
addMemory(memory: string | readonly BmxTmEntry[], origin?: string) => Promise<number> |
Add translations to the memory: TMX text or entries. Returns how many were added. |
getChangeCount |
getChangeCount() => Promise<number> |
How many strings changed since the file was read or saved. |
getDocument |
getDocument() => Promise<BmxL10nDocument | null> |
The document: its units with their translations and states. |
getIssues |
getIssues() => Promise<BmxL10nIssue[]> |
Every problem the checks find now. |
getMemory |
getMemory() => Promise<string> |
The memory's entries (loaded, and confirmed here), as TMX. |
getProgress |
getProgress() => Promise<ReturnType<typeof documentProgress> | null> |
Units and words by state. |
getText |
getText() => Promise<string> |
The translated file's text, written in its format (only translated strings changed). |
load |
load(options: BmxTranslationLoadOptions) => Promise<BmxTranslationLoadDetail | null> |
Read a translation: { target } for a bilingual file, { source, target? } for a one-language format (with targetLanguage for a new translation). |
loadFiles |
loadFiles(files: readonly File[] | FileList) => Promise<BmxTranslationLoadDetail | null> |
Read files the person chose or dropped: translation files, and TMX memories and TBX or CSV glossaries among them. |
pretranslate |
pretranslate() => Promise<number> |
Fill empty strings from 100% memory matches. Returns how many were filled. |
pseudoTranslate |
pseudoTranslate() => Promise<number> |
Fill empty strings with a pseudo-translation, to test a user interface. Returns how many. |
redo |
redo() => Promise<boolean> |
|
save |
save(fileName?: string) => Promise<boolean> |
Download the translated file (cancel bmxSave to handle it yourself). |
selectUnit |
selectUnit(id: string) => Promise<boolean> |
Select a string by its id. |
setGlossary |
setGlossary(glossary: string | BmxGlossary | null, fileName?: string) => Promise<void> |
Use a glossary: TBX or CSV text (with a file name to tell which), or one read with readGlossary. |
setTranslation |
setTranslation(id: string, target: string | Record<string, string>, state?: BmxL10nState) => Promise<boolean> |
Change a string's translation (and state) as if typed, with undo. |
undo |
undo() => Promise<boolean> |
Slots
| Slot | Description |
|---|---|
description |
Rich help text, in place of the description property. |
label |
Rich label content, in place of the label property. |
CSS shadow parts
| Part | Description |
|---|---|
description |
The help text under the field. |
label |
The label element. |
list |
The list of strings. |
menu |
The More menu. |
progress |
The progress bar. |
row |
A string in the list; also row-selected. |
side |
The memory, terms, checks and preview panel. |
source |
The source text. |
status |
The bar under the editor. |
target |
A translation field. |
toolbar |
The bar of commands and filters. |
unit |
The selected string's source, translation and state. |
CSS custom properties
| Property | Description |
|---|---|
--bmx-translation-editor-background |
The editor's background. |
--bmx-translation-editor-height |
The height of the list and editor on a wide screen. |
--bmx-translation-editor-label-font-size |
The label's font size. |
--bmx-translation-editor-list-height |
The list's height on a narrow screen, where the editor sits under it. |
--bmx-translation-editor-placeholder-background |
Placeholders such as %s and {name}. |
--bmx-translation-editor-placeholder-color |
Placeholders' text. |
--bmx-translation-editor-radius |
Corner radius of the frame. |
--bmx-translation-editor-row-height |
The height of one string in the list. |
--bmx-translation-editor-selected-background |
The selected string in the list. |
--bmx-translation-editor-support-font-size |
Font size of the description and error message. |
--bmx-translation-editor-tag-background |
Inline tag tokens. |
--bmx-translation-editor-tag-color |
Inline tag tokens' text. |
--bmx-translation-editor-term-color |
The underline of glossary terms. |