Translate filter reference
The translate page is where you read and edit your project's strings. The filter bar sits across the top, and it's the fastest way to narrow several thousand keys down to the few you need to act on. This page explains every filter — what it shows, when to reach for it, and the rule it actually applies.
If you're new to BeMyWords, the short version: pick a target language, pick a namespace, then layer filters until the visible rows are exactly the work in front of you. Filters update the view in your browser; nothing is saved or sent until you edit a translation.
How filters combine
Filters compose with AND, not OR. A row has to satisfy every active filter at once to stay visible.
- Status is a radio group — exactly one is active. All (the default) means status is not narrowing the view.
- Pills (the second row) are toggles. Activate as many as you want; each one further narrows the visible rows.
- Search, Updated, Created, Edited by, and Locked by are independent narrowers and stack on top of everything else.
- Namespace and Target language are scope selectors that change the URL — selecting a different value reloads the page with a fresh, filtered dataset.
To clear everything, set status back to All, click any active pills off, clear the search box, and reset the dropdowns to Any.
Search
A single text box at the top of the filter bar. Type a few characters and the row list narrows immediately, with a 150 ms debounce so it doesn't fight your typing.
The search is case-insensitive and matches anywhere in three places on each row:
- The translation key (e.g.
home.hero.title). - The source-language value.
- The current value in the target language, including unsaved edits.
The search is plain substring — no regex, no quoted phrases, no field prefixes. If you want to find every key under checkout.errors.*, type checkout.errors.
Status
The radio group on the right of the first filter row. Use it as your primary cut: the work-state of the row in this language.
All
The default. Status is not narrowing the view.
Translated
Rows that have a non-empty value in the target language. Approved, AI-drafted, and needs-review translations all count as translated for this filter — Translated answers "is anything saved here?", not "is it good?".
Untranslated
Rows whose target value is missing entirely. This is your queue when you're catching up on a new key batch.
AI Draft
Rows where the most recent translation came from an AI batch and hasn't been reviewed by a human yet. These are safe defaults but should be reviewed before you ship a release where copy quality matters.
Needs Review
Rows a teammate has explicitly flagged as needing a second look — distinct from AI Draft, which is a default state. Needs Review is something a person set deliberately.
Approved
Rows whose translation has been signed off. The strongest "ready to ship" signal in the system.
Unused 90d+
Translated rows whose key hasn't been requested by a build in the last 90 days. Build-time tracking is what populates this — every fetch from your project's API endpoint stamps a last_used_at on the keys it returned, so this filter shows strings the running app no longer asks for.
Useful when you're trimming a project that's accumulated dead keys over years. Hide the suspects with the per-key hide control, then export to confirm the file shrank.
Source drifted
Rows whose source value has changed since the target translation was last saved. The translation might be technically correct but stale. Only appears when you're looking at a non-source language; on the source tab there's nothing to be drifted from.
The Reports page has a "Translate drift" job that fixes these in bulk; this filter lets you triage them by hand instead.
Empty
Rows whose target value is missing or contains a placeholder that means "not done yet" — TODO, TBD, ..., or [...] (case-insensitive). A subset of Untranslated in spirit, but it also catches rows where someone typed a placeholder and moved on.
Content pills
The second filter row. Each pill is an independent toggle that further narrows the rows that already passed the status filter and search. Pills are AND-ed: enabling Variables + Long shows only rows that have a {{variable}} and are over 100 characters.
A pill matches the source value, the target value, or both, depending on what's meaningful for the check. The exact rules are spelled out below so you don't have to guess.
Variables
Rows whose source or target contains {{...}} placeholders. Use it before a release to spot-check that every variable in the source survived translation and didn't get dropped, renamed, or accidentally translated.
Title case
Rows whose target value contains two or more consecutive Title-Cased Words (each starting with an uppercase letter, each at least 2 characters). Useful in languages where title case is wrong by default — Norwegian, French, German — and you want to catch translations that copied English casing conventions.
URLs
Rows where the source or target contains a URL. Either a full https://example.com or a bare domain ending in .com, .org, .net, .io, or .no. Useful for double-checking that links survived translation unchanged.
Email addresses don't count as URLs — support@example.com matches the Emails pill, not this one, even though example.com is a valid bare-domain match in isolation.
Emails
Rows where the source or target contains text that looks like an email address — anything with both @ and .. The check is loose on purpose; treat it as "probably an email here" rather than RFC-perfect.
Numbers
Rows where the source or target contains any digit. Pair it with a language change to find places where number formatting (decimal commas, thousands separators) might need attention.
HTML
Rows where the source or target contains both < and >. Inline markup, embedded tags, mailto links — anything that suggests the string isn't pure prose. Strings with HTML often need extra care because translators can break the markup.
CAPS
Rows whose target value is more than 50% uppercase letters. Catches accidental shouting and acronyms that should be cased per the target language's conventions.
Long
Rows whose target is longer than 100 characters. Useful when you're worried about UI overflow — long German translations under tight English mockups are a classic.
Short
Rows whose target is between 1 and 9 characters. The complement of Long. Helpful when reviewing buttons, labels, and microcopy where length is meaningful.
Fullstop
Rows where the source or target ends with a period — ., 。 (CJK), or . (full-width). Use it to enforce sentence-style punctuation conventions, or to find places where someone added a stray full stop to a button label.
TODO marker
Rows whose source or target contains FIXME, TODO, XXX, or [!] — the markers a developer leaves in the code when they want to come back to a string. Distinct from the Needs Review status (a workflow signal a teammate sets deliberately); this one matches text content authored at code-write time.
Comments
Rows whose source or target looks like it has stray code comments — //, /*, <!--, or a # after whitespace. Often a sign that something pasted code into a translation.
Any scheme:// prefix (https://, ftp://, git://, file://, etc.) is stripped before the // check, so URLs don't trip the heuristic.
Same as source
Rows whose target value is exactly equal to the source value (after trimming whitespace). For most languages this is a strong "this didn't get translated" signal — though some languages legitimately share words with the source (e.g. email, test in many European languages).
Only appears when you're looking at a target language that isn't the project's source language. On the source-language tab everything is "same as source" by definition, so the filter would be useless.
Wrong terms
Rows that violate one of the project's terminology rules. Two cases match:
- The target contains a forbidden term from the project's substitution rules.
- The source contains a glossary source-term, but the target doesn't contain the matching target-term.
Set up forbidden terms and glossary entries under the project's Settings page. The pill is rendered red because it's a deliberate quality gate.
Has history
Rows whose translation has at least one prior revision. Useful when you're trying to find strings that have been edited (vs. accepted on first save), e.g. when investigating regressions.
DNT
Rows whose key is on the project's "do not translate" list. Brand names, product names, and technical identifiers usually live here. Helpful when auditing whether the DNT list is actually being respected.
Locked
Rows whose translation is locked in the current target language. Locked rows can't be edited until the lock is released, including by AI bulk jobs. The lock is per-language, so a row can be locked in Norwegian and editable in Swedish.
Not locked
The complement of Locked — rows you can edit right now in this language. Useful when planning a bulk job and you want to see exactly what it could touch.
Dates
Two independent dropdowns near the right of the second filter row. Both default to Any, which means "don't narrow on this date".
The available ranges are:
- This week — from the most recent Sunday at 00:00 local time to now.
- This month — from the 1st of the current month to now.
- This quarter — from the 1st of the current quarter (Jan 1 / Apr 1 / Jul 1 / Oct 1) to now.
- This year — from January 1 of the current year to now.
Updated
Filters by the row's last-saved timestamp on the target-language translation. Use it to scope yourself to "what changed this week" or to find recently-touched strings before a release cut.
Created
Filters by when the key was first added to the project. Useful for finding new keys that came in with a fresh import or a recent feature branch.
People
Two more dropdowns next to the date filters. Both populate from the actual list of people who've edited or locked rows in the current view.
Edited by
Show only rows whose most recent target-language edit was made by the selected user. Useful for self-review ("show me what I edited this week") or for triaging a teammate's work.
Locked by
Show only rows currently locked by the selected user. Helpful when several people are working in parallel and you want to see who owns what.
Scope
Two server-side selectors that change the URL rather than filtering in place. They live above the filter bar.
Namespace
Each project is split into namespaces — typically one per major area of the app (marketing, checkout, account, etc.). Selecting a namespace reloads the page with only that namespace's keys; selecting All namespaces shows everything.
The reason this is server-side rather than a client-side toggle is that namespaces can each contain thousands of keys, and shipping all of them to the browser at once would be slow.
Target language
The "Language" dropdown above the filter bar swaps which target you're editing. Switching languages reloads the page because the available filters (e.g. Source drifted, Same as source) and the row metadata depend on which language you're in.
Frequently asked
What does the Empty filter catch that Untranslated doesn't?
Untranslated matches rows with no value at all. Empty additionally matches rows where someone typed a placeholder like TODO, TBD, ..., or [...] and moved on.
If you're trying to find every row that needs work, Empty is usually the safer choice.
What's the difference between AI Draft and Needs Review?
AI Draft is the default state of any string written by a bulk AI job — a marker that says "this hasn't been reviewed". Needs Review is set by a human, deliberately, to flag a string for follow-up. Both are valid pre-ship states; combine them when you want to see everything that's not yet approved.
Why is "Source drifted" only sometimes visible?
It's hidden when you're on the source-language tab — there's nothing to be drifted from. It only makes sense for a target language whose translation was saved against an older version of the source.
How is "Unused 90d+" calculated?
Every time your build pulls translations from the BeMyWords API, the keys it requests get a last_used_at stamp. Unused 90d+ shows translated rows whose stamp is older than 90 days, or which have never been requested by a build at all. It's a build-time signal, not a runtime one — if you stop deploying, everything will drift into "unused" eventually.
How do I clear all filters at once?
There isn't a single "Clear all" button. Set the status group back to All, click any active pills off, blank the search box, and reset the date / editor / locker dropdowns to Any. Switching namespace or language reloads the page and clears the client-side state too.
Do filters work the same in spreadsheet mode?
Yes. The spreadsheet view uses the same filter bar and the same rules — it just renders the matching rows as a table instead of a tree.
Do filters survive a page reload?
Most do, for the lifetime of the browser tab. The status, pills, search query, date dropdowns, and editor / locker selections are persisted to per-tab session storage and re-applied when you reload the same project. Editor and locker selections only restore if the chosen person still appears in the current scope's dropdown — otherwise they reset to Any. Closing the tab clears everything; opening a new tab gives you a fresh filter state.