Simple and powerful DOM translator.
About
With this package you may implement translation for any elements on web page like "Google Translate Widget" and make site multi lingual even if no localization system is designed yet.
Some of key features
- Battle tested solution. This package is used by a popular browser extension Linguist Translate with over 200k active users.
- DOM translator is a high performance solution
- Modular design let you tune any part DOM translator
- Translation of web pages with dynamical changes is supported
- Highly configurable design let you start translate any document in minutes
Screenshot of Live Demo
Usage
Install with npm install domtranslator
Check out Live Demo to see a real world usage example with a real translator.
Simple example with dummy translator is
import { PersistentDOMTranslator, DOMTranslator, NodesTranslator, IntersectionScheduler } from 'domtranslator'; import { createNodesFilter } from 'domtranslator/utils/nodes'; // Dummy translator const translator = async (text) => '[translated] ' + text; // `PersistentDOMTranslator` will translate updated nodes with use `DOMTranslator` const domTranslator = new PersistentDOMTranslator( new DOMTranslator( // Nodes will be translated with fake translator, // that is just adds a text prefix to original text new NodesTranslator(translator), { // When `scheduler` is provided, a lazy translation mode will be used. // Nodes will be translated only when intersects a viewport scheduler: new IntersectionScheduler(), // Filter will skip nodes that must not be translated filter: createNodesFilter({ // Only listed attributes will be translated attributesList: [ 'title', 'alt', 'placeholder', 'label', 'aria-label', ], // Any elements not included in list will be translated ignoredSelectors: [ 'meta', 'link', 'script', 'noscript', 'style', 'code', 'textarea', ], }), } ), ); // You may translate whole document domTranslator.translate(document.documentElement); // Or just few elements // domTranslator.translate(document.querySelector('#widget1')); // domTranslator.translate(document.querySelector('#widget2')); // domTranslator.translate(document.querySelector('#widget3')); // You may disable translation for any element, and restore its original text domTranslator.restore(document.documentElement); // Or for specific element // domTranslator.restore(document.querySelector('#widget2'));
So if you would run this code against next HTML document
<body> <div> <p> Hello <strong>Jake</strong>, welcome back! </p> <p> <a href="#">Check feedback</a> for your <i title="Epic DnB drop">recent work</i>. </p> </div> </body>
Your result would be
<body> <div> <p> [translated] Hello <strong>[translated] Jake</strong>[translated] , welcome back! </p> <p> <a href="#">[translated] Check feedback</a>[translated] for your <i title="[translated] Epic DnB drop">[translated] recent work</i>. </p> </div> </body>
API
NodesTranslator
The NodesTranslator class is purposed to translate text nodes.
To translate element with its nested text nodes and attributes, use DOMTranslator.
Usage example
import { NodesTranslator } from 'domtranslator'; const nodesTranslator = new NodesTranslator(translator); const altAttr = document.querySelector('img').getAttribute('alt'); nodesTranslator.translate(altAttr);
In this example we get Attr node of image and translate it.
Below listed documentation for class methods.
constructor(translator: (text: string, score: number) => Promise)
Translator is a callback that will be called to translate node text.
Callback will be called when node is translates first time and when node is updated and must be translated again.
Arguments passed to a callback is
text- a node text for translationscore- a measured importance score of node, based on its position in document, its content and context. The greater number - the more important node. This is optional parameter to use, if you need to schedule translating based on node importance score.
translate(node: Node, callback?: ProcessedNodeCallback): void
Method run node text translation.
If callback is provided, it will be called once node will be translated. Target node will be passed in arguments.
This method may be called only once for one node, otherwise throws error.
update(node: Node, callback?: ProcessedNodeCallback): void
Method run translation for node text if node is already processed. Otherwise throws error.
Works equal to translate method.
restore(node: Node): void
Method restores original node text
has(node: Node): boolean
Method returns boolean that reflect translation state of node.
If returned true - it means node is translated.
getState(node: Node): NodeTranslationState | null
If node is translated, this method returns node state, otherwise null will be returned.
The state includes original text of node if node translated or null in case node is in pending of translation.
export type NodeTranslationState = { originalText: string | null; };
DOMTranslator
The DOMTranslator class works equal to NodesTranslator but
- it recursive translates whole DOM tree passed to its methods
- can translate nodes lazy
- can filter nodes to skip those that should not be translated (like URLs or class names, etc)
constructor(nodesTranslator: NodesTranslator, config: Config)
A nodesTranslator is an instance of NodesTranslator.
Config is described by next type
export type Config = { /** * If is provided, nodes can be translated delayed - after intersect the viewport */ scheduler?: DOMTranslationScheduler; /** * Determines which nodes should be translated */ filter?: (node: Node) => boolean; };
When scheduler option is provided, translation will be run in lazy mode that depends on implementation of DOMTranslationScheduler.
You may provide instance of IntersectionScheduler to translate only nodes in browser viewport.
If node is out of viewport, it will be not translated automatically. Instead IntersectionObserver will start watch over node, and once it will intersect viewport, translation will be started.
When filter option is provided, it will be called for each node and in case callback will return false, node will be not translated.
Keep in mind that filter will be called for every Node, not Element. If you have only 1 element on page with 5 attributes and text inside, callback will be called 6 times for every of mentioned nodes.
You may use createNodesFilter util as shown in example above, to configure callback to filter out nodes.
import { createNodesFilter } from 'domtranslator/utils/nodes';
Or, you may implement this logic yourself.
PersistentDOMTranslator
Translates DOM tree persistently.
Unlike DOMTranslator, when nodes in tree is updates, it will be automatically translated.
This feature is implemented with MutationObserver and protected against recursion when translated node trigger update, that translate node and trigger update.
constructor(readonly translator: IDomTranslator)
Constructor expects instance of DOMTranslator or another object that implement its interface.
Support us
This project follows a FOSS principles. If you like this package spread the word about it.
Star repo on GitHub, share it on social media.
You also may support a project with your contributions. Create issue or pull request in our repo.
