Regex-based i18n tools miss strings and flag false positives. We parse your code with tree-sitter to understand the AST — here's why that matters.
The first step in any i18n workflow is detection: figuring out which strings in your codebase are user-facing and need translation. Get this wrong and everything downstream breaks — you either miss strings that show up untranslated in production, or you waste time and money translating strings that were never meant to be seen by users.
Most i18n tools use regular expressions for detection. On the surface this seems reasonable — strings are just text between quotes, right? In practice, regex-based detection is deeply unreliable, and the reasons why tell you a lot about why building good developer tools is hard.
Consider a typical React component. It might contain a heading like <h1>Welcome back</h1>, an import like import "./styles.css", a CSS class like className="flex items-center", and a configuration constant like const API_URL = "https://api.example.com". A regex that matches quoted strings will flag all four. Only the first one should actually be translated.
You can try to patch this with negative lookaheads, allowlists, and heuristics, but you're fighting a losing battle. The fundamental problem is that regex operates on text, not structure. It can't tell the difference between a string that's a child of a JSX element and a string that's an import specifier, because it doesn't understand the syntax tree.
Tree-sitter is an incremental parsing library that can build a concrete syntax tree for your source code. Unlike a full compiler frontend, it's designed to be fast, error-tolerant, and language-agnostic. It's what powers syntax highlighting in editors like Neovim, Zed, and GitHub, and it's what we use in Polyglot to understand your code structure.
When Polyglot scans a file, it parses the entire file into a tree-sitter AST and then walks the tree recursively. At each node, it makes a decision based on the node's type and its position in the tree. This gives us context-aware detection that regex can't match. The output is a list of candidates for you to review — not proof that every UI string was found.
Polyglot's detection engine recognizes several categories of translatable content:
<p>Hello world</p>placeholder, aria-label, title, and altAlert.alert() in React NativeDetection is only half the problem. The other half is filtering out strings that look like text but aren't user-facing. Polyglot applies multiple layers of filtering after initial detection:
className props are skipped entirelyDifferent frameworks structure their code differently, and a detection engine needs to account for that. Polyglot has dedicated handling for each supported framework:
Astro components split into frontmatter (between --- fences) and template. Polyglot detects the fences, skips the frontmatter script, blanks out <style> and <script> blocks in the template, then parses the template by wrapping it in a JSX fragment. Line numbers are adjusted so reported positions match the original file. Strings that only live in frontmatter aren't reported, so check those by hand.
SvelteKit templates use special syntax like {#if}, {#each}, and {expression} that aren't valid JSX. Polyglot strips <script> and <style> blocks, cleans the SvelteKit-specific syntax, and wraps the result in a JSX fragment for tree-sitter to parse. Line numbers are preserved through the transformation.
React Native introduces translatable strings in places that web frameworks don't — API calls like Alert.alert("Title", "Message") and ToastAndroid.show("Done!"), as well as RN-specific props like accessibilityLabel and headerTitle. When Polyglot detects a React Native project, it enables additional extraction rules for these patterns while filtering out noise from StyleSheet.create, Platform.select, and similar non-user-facing calls.
Structure-aware detection cuts out much of the noise regex can't avoid: class names, import paths, URLs, and most config constants are filtered by where they sit in the tree or by pattern. It's not perfect. Results are candidates, some need a human call, and a clean scan isn't proof of full UI coverage. Treat the output as a review list, not a verdict.
The scanner is also quick. Tree-sitter parses in roughly linear time relative to file size, and polyglot scan works through files in parallel across your CPU cores. Separately, polyglot translate keeps file content hashes in .polyglot-cache.json, so it only sends new or changed strings for translation.
The detection engine itself is proprietary, but it's free to use for unlimited scanning — no account required. What we do publish openly is the GitHub Action that runs it in CI and the harness behind our translation quality benchmark, both MIT-licensed on GitHub, so you can inspect how they work and wire detection into your own pipeline.
Start in your terminal
Install the CLI, run a scan, and see exactly what you're missing. Free, no account required.