Changelog
What we shipped, when.
Hand-curated. Tags: New / Improved / Fixed.
Changelog
What's new in BeMyWords. Most recent first.
2026-05-17 — Script-mode hardening and source-edit retranslation
- New: zero-config language switcher. A script-mode page that places no
[data-bw-switcher]element now gets a thin full-width language footer auto-injected at the bottom of the page, styled to blend with the site's existing footer. Languages show their own endonym (English, Norsk, Русский, Українська), the current one plain and the rest as underlined links. Pages that place[data-bw-switcher]still get the dropdown there;data-switcher="off"on the embed opts out. The dropdown switcher now labels languages by endonym too, not language codes. - Fixed: bulk translate failed every key when triggered outside a web request.
Work::BulkTranslateJobreconstructed the project fromCurrent.project, which is empty when bulk translate runs from a background job (e.g. auto-translate after a sync) — so every batch crashed withNoMethodErrorand the run reported all keys failed. The job now takes the project from its persisted job record. - Fixed: the source language is served and editable like any other language. In script mode the original page text is the translation key; every language — English included — is an editable value. The public-read dictionary now serves the source language too, so source-value edits made in BeMyWords reach source-language visitors; the in-page editor opens on the source locale for script-mode projects; and the embed script falls back to the project's source language when a page carries no locale signal.
- Fixed: the in-page editor works on a project that has no namespace yet. A brand-new script-mode project has no namespace until its first harvest. The editor used to fatal-error when loading translations 404'd, so "↺ Sync page strings" silently did nothing. Now a missing namespace is treated as empty (the editor opens normally), and the Sync harvest creates the namespace on demand.
- New: Nightly site sync. Script-mode projects can set a site root URL under Settings → Integration; BeMyWords crawls the site's sitemap nightly (02:00 UTC), harvests new source strings, and auto-translates them — no need to open the in-page editor.
- New: Re-translate edited strings. A per-project opt-in under Settings → Integration. When on, editing an existing source string re-runs AI translation on that string's now-stale target translations on the next debounced pass —
ai_drafttranslations are refreshed, human-approved ones are left for manual drift review. - Fixed: editing a source string now triggers auto-translate. The
/api/overwritepath never enqueued an auto-translate pass, so source edits didn't propagate to target languages. It now does, consistent with/api/missingand/api/sync. - Fixed:
sync_page_stringsno longer 500s under concurrent syncs. Two simultaneous Konami "↺ Sync page strings" runs on the same page raced on the unique key index; each row create is now wrapped in its own savepoint, so a row already created by a parallel sync is simply counted as skipped. - Fixed: bloated dictionary payloads on slug-keyed projects. The public dictionary no longer ships a redundant
slug → translationentry for every key; the secondary index is kept only for harvested page text, which never matches a slug. - Fixed: empty locale switcher for projects without an explicit enabled-languages list —
/api/<project>/public_infonow falls back to the languages that actually have translations. - Improved: bw-editor
trigger.jscleanups — removed a dead__title__lookup and aligned locale-prefix regexes.
2026-05-13 — Script mode for Tilda and other hosted sites
- New: Script-mode projects. Pick Settings → Integration → Script (Tilda, hosted sites) to mark a project as runtime-swap rather than build-time. Source text becomes the translation key by convention. The GitHub redeploy tab and footer are hidden — they don't apply.
- New: Integrate page.
/projects/:id/integrateshows a copy-pasteable embed snippet and a live install indicator that turns green within seconds of the script reaching a visitor's browser. See integrations guide. - New: Locale switcher widget. Drop
<div data-bw-switcher></div>anywhere on the page; the script renders a styled<select>populated from the project's enabled languages and flips the URL's?lang=param on change. - New: ↺ Sync page strings button in the Konami editor (source-language tab). One-click bulk-imports every text node, attribute (
alt,title,aria-label,placeholder), and submit-button value on the live page as source rows. Idempotent. Backed byPOST /api/<project>/sync_page_strings/<namespace>. - New: Auto-translate on save. Per-target opt-in under Settings → Integration. When a source row is created or edited, BeMyWords schedules a debounced (5s) AI pass for every opted-in target language. Drafts land as
ai_draftand are immediately visible to visitors. - New: Instant dictionary cache via ETag — no CDN required. The public dictionary endpoint serves
max-age=0, must-revalidateplus an ETag derived from a per-namespacedictionary_versionbumped on every write. Visitors get edits on their next page load; revalidation returns304 Not Modified(a few hundred bytes of headers) when nothing changed. - New: Rate limiting on the new public endpoints —
api/install_ping(60/min/IP),api/public_info(120/min/IP),api/sync_page_strings(60/min/project). - Improved:
api/missing, the editor save paths,api/sync, andsync_page_stringsall flow through oneTranslations::EnqueueAutoTranslatehelper, so the auto-translate trigger is consistent across surfaces. - Fixed: Pre-existing audit-resolve spec failures from a Ruby 3 kwargs change.
2026-04-25 — Marketing site, quality scans, ephemeral screenshots
- New: Public site at www.bemywords.no — landing, docs for 8 integrations, this changelog, and legal.
- New: Private beta — pilot customers email support@skiwo.com for an invite. Public pricing held until we're out of beta.
- New: Language Audit — scan every translation in a language; Claude flags strings whose actual language doesn't match (English text living in the Norwegian column, etc.) at confidence ≥ 70%. Read-only; results render in the Reports view.
- New: Unified Reports — checkup and language-audit reports share one listing, with a one-click "Filter translate panel to these N keys" CTA so you can walk through a run's suspects without losing the rest of the page.
- Improved: Language-tab progress percentages now floor instead of rounding. 999/1000 reads 99%, never 100%, until every key is translated. No more "100%" tabs that secretly have a missing string.
- Improved: Screenshots are ephemeral by design. Image bytes go to Claude in flight, then to your browser only (8-hour IndexedDB cache). Never on our servers, in our database, or in our logs. Screenshot cards and show pages now show the uploader and tell other team members why the image isn't visible from their browser.
2026-04-24 — Integration guides
- New: Integration guides for Astro, Rails i18n, React + i18next, Next.js (App Router + Pages Router), Vue 3 (vue-i18n), iOS (
.strings), Android (strings.xml), and Flutter (ARB +intl). Astro and Rails are production-verified by Skiwo's own sites; the rest are best-effort drafts pending real-world deploys. - New: Product roadmap published — phased plan from pilot through public launch. Positioning confirmed: cheap + simple for solo devs and small teams, not competing on enterprise features.
2026-04-22 — Pilot-readiness
- Improved: Puma upgraded from 4.1 to 6.6.1 — current support, faster cluster restarts, no more EOL servers in front of customer data.
- Improved: Rails upgraded from 6.0.6.1 to 6.1.7.10. Final 6.0 patches were 2023; 6.1.7.10 has the security fixes.
- Improved: Cross-tenant isolation test coverage broadened from 3 cases to 23, covering every multi-tenant boundary in the API and dashboard.
- New: Rate limiting on API writes and the public
/changesendpoint via rack-attack. - New: Prune rake tasks for unbounded-growth tables (versions, AI usage logs).
- Fixed: Drifted specs repaired — full RSpec suite green at 454/454.
2026-04-21 — Click-to-translate editor
- New: In-page editor. Drop a
<script src=…>on your site and click any string to edit it. Saves push to BeMyWords; the page swaps to your new translation without a rebuild. - New: Candidate picker for ambiguous strings (same word repeated in multiple keys). Keyboard-navigable; sorted by area; flips above the click on tight viewports.
- New: Live-swap of translations edited since the site's last deploy — your edits show up immediately, not at next build.
- Fixed: Safari shadow-DOM selection defence — drag-select on Safari no longer tunnels into editor chrome.
- Fixed: Mixed-content paragraph match via
caretPositionFromPoint/caretRangeFromPoint. Paragraphs containing inline tags (<a>,<strong>,<em>) now resolve correctly to a single key. - Fixed: Block anchor navigation while the editor is active — clicks on links no longer leave the page mid-edit.
Earlier
This is the first public changelog. Commits going back further (multi-tenant rebuild, translation memory, glossary / DNT / substitution rules, screenshots, bulk AI translation, version history) are tracked in git.