{"id":2224,"date":"2026-09-03T22:12:09","date_gmt":"2026-09-03T22:12:09","guid":{"rendered":"https:\/\/damosenelblanco.com\/free-catalan-spell-checker-why-we-built-it\/"},"modified":"2026-09-03T22:12:09","modified_gmt":"2026-09-03T22:12:09","slug":"free-catalan-spell-checker-why-we-built-it","status":"publish","type":"post","link":"https:\/\/damosenelblanco.com\/en\/free-catalan-spell-checker-why-we-built-it\/","title":{"rendered":"Writing well in Catalan should not depend on a subscription"},"content":{"rendered":"<p>For years, the LanguageTool extension did something quiet and valuable: it underlined Catalan mistakes while you wrote an email, a tweet or a comment. In January 2025 it went Premium-only. A fourteen-day trial and then, unless you pay, the underlines disappear. <b>For anyone writing in Catalan, that was not losing a feature: it was losing the only decent checker there was in the browser.<\/b><\/p>\n<blockquote class=\"deb-art__pull\"><p>A basic tool for a language should not be able to vanish with a pricing change.<\/p><\/blockquote>\n<h2>What broke and what did not<\/h2>\n<p>Access broke, not the technology. The LanguageTool engine is still free software (LGPL 2.1) and the Catalan rules \u2014 more than 2,500 of them, maintained by Softcatal\u00e0 and the community \u2014 are among the best that exist for our language: they catch agreement errors, weak pronouns, barbarisms and Spanish calques that an ordinary spell checker never sees. What stopped being accessible was the piece connecting that engine to the text box where you write.<\/p>\n<h2>Putting the pieces back together<\/h2>\n<p>So that is exactly what we did. <a href=\"https:\/\/corrector.damosenelblanco.com\/\" target=\"_blank\" rel=\"noopener\">Corrector Catal\u00e0<\/a> has three parts, and all three are open.<\/p>\n<p><b>A server.<\/b> The free LanguageTool engine running on the studio&#8217;s own server, with an API compatible with the official one (<code>v2\/check<\/code>) and a reasonable limit of 30 requests per minute per IP. If you would rather run your own, Docker and nginx get it up in five minutes.<\/p>\n<p><b>A brand-new Chrome MV3 extension, free forever.<\/b> It underlines errors in text areas, search boxes, comments and editors such as Gmail, Reddit, X or WhatsApp Web. It works on Chrome, Edge, Brave and Vivaldi. Synced personal dictionary and domain list; automatic dark mode.<\/p>\n<p><b>A landing page with a live demo.<\/b> Paste a text and within 0.7 seconds you see the same corrections the extension will make.<\/p>\n<div class=\"deb-figwrap\"><figure class=\"deb-fig\" data-deb-chart><figcaption class=\"deb-fig__head\"><span class=\"deb-fig__title\">Where your text travels \u00b7 and where it does not<\/span><\/figcaption>\t\t<div class=\"deb-fig__scroll\">\n\t\t\t<svg viewBox=\"0 0 760 406\" role=\"img\" aria-label=\"Three-step diagram: the content script reads what you type on the page, the extension service worker batches it and sends it over HTTPS to the LanguageTool server you choose, which returns suggestions. There is no analytics, no cookies and no third party at any step.\">\n\t\t\t\t<defs>\n\t\t\t\t\t<marker id=\"deb-fig-arr\" viewBox=\"0 0 10 10\" refX=\"9\" refY=\"5\" markerWidth=\"8\" markerHeight=\"8\" orient=\"auto-start-reverse\"><path d=\"M0,0 L10,5 L0,10 Z\" class=\"deb-fig__arrowhead\"\/><\/marker>\n\t\t\t\t<\/defs>\n\t\t\t\t\t\t\t\t\t<rect class=\"deb-fig__node\" x=\"14\" y=\"16\" width=\"732\" height=\"78\" rx=\"10\"\/>\n\t\t\t\t\t<text class=\"deb-fig__node-n\" x=\"32\" y=\"42\">01<\/text>\n\t\t\t\t\t<text class=\"deb-fig__node-t\" x=\"32\" y=\"68\">Where you type<\/text>\n\t\t\t\t\t\t\t\t\t\t<text class=\"deb-fig__node-s\" x=\"314\" y=\"50\">Gmail, Reddit, X, WhatsApp Web\u2026<\/text>\n\t\t\t\t\t\t\t\t\t\t<text class=\"deb-fig__node-s\" x=\"314\" y=\"68\">the content script reads the text<\/text>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<path class=\"deb-fig__arrow\" d=\"M62,100 L62,134\" marker-end=\"url(#deb-fig-arr)\"\/>\n\t\t\t\t\t<text class=\"deb-fig__annot\" x=\"76\" y=\"121\">text \u2193<\/text>\n\t\t\t\t\t<path class=\"deb-fig__arrow\" d=\"M698,134 L698,100\" marker-end=\"url(#deb-fig-arr)\"\/>\n\t\t\t\t\t<text class=\"deb-fig__annot\" x=\"684\" y=\"121\" text-anchor=\"end\">\u2191 suggestions<\/text>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<rect class=\"deb-fig__node\" x=\"14\" y=\"140\" width=\"732\" height=\"78\" rx=\"10\"\/>\n\t\t\t\t\t<text class=\"deb-fig__node-n\" x=\"32\" y=\"166\">02<\/text>\n\t\t\t\t\t<text class=\"deb-fig__node-t\" x=\"32\" y=\"192\">The extension<\/text>\n\t\t\t\t\t\t\t\t\t\t<text class=\"deb-fig__node-s\" x=\"314\" y=\"174\">service worker: batches, waits 0.7 s<\/text>\n\t\t\t\t\t\t\t\t\t\t<text class=\"deb-fig__node-s\" x=\"314\" y=\"192\">and checks the cache<\/text>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<path class=\"deb-fig__arrow\" d=\"M62,224 L62,258\" marker-end=\"url(#deb-fig-arr)\"\/>\n\t\t\t\t\t<text class=\"deb-fig__annot\" x=\"76\" y=\"245\">HTTPS \u2193<\/text>\n\t\t\t\t\t<path class=\"deb-fig__arrow\" d=\"M698,258 L698,224\" marker-end=\"url(#deb-fig-arr)\"\/>\n\t\t\t\t\t<text class=\"deb-fig__annot\" x=\"684\" y=\"245\" text-anchor=\"end\">\u2191 suggestions<\/text>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<rect class=\"deb-fig__node deb-fig__node--accent\" x=\"14\" y=\"264\" width=\"732\" height=\"78\" rx=\"10\"\/>\n\t\t\t\t\t<text class=\"deb-fig__node-n\" x=\"32\" y=\"290\">03<\/text>\n\t\t\t\t\t<text class=\"deb-fig__node-t\" x=\"32\" y=\"316\">Your LanguageTool server<\/text>\n\t\t\t\t\t\t\t\t\t\t<text class=\"deb-fig__node-s\" x=\"314\" y=\"298\">ours or the one you self-host<\/text>\n\t\t\t\t\t\t\t\t\t\t<text class=\"deb-fig__node-s\" x=\"314\" y=\"316\">v2\/check API \u00b7 returns suggestions<\/text>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<line class=\"deb-fig__grid\" x1=\"14\" x2=\"746\" y1=\"364\" y2=\"364\"\/>\n\t\t\t\t<text class=\"deb-fig__val\" x=\"380\" y=\"392\" text-anchor=\"middle\">0 analytics \u00b7 0 cookies \u00b7 0 third parties \u00b7 the text only goes to the server you choose<\/text>\n\t\t\t<\/svg>\n\t\t<\/div>\n\t<\/figure>\n\t<\/div>\n<h2>What it does (and what it does not)<\/h2>\n<p>It automatically detects which variant you are writing in \u2014 standard Catalan, Valencian, Balearic or Spanish \u2014 and checks in that variant; you can also pin it manually. It does not rewrite your style, it does not paraphrase with AI and it does not suggest &#8220;improving your tone&#8221;: it checks spelling and grammar, which is exactly what had been lost.<\/p>\n<p>It is also not yet on the Chrome Web Store (the listing is under review) or on Firefox (the build is on its way). In the meantime, installation is manual: download a zip, open <code>chrome:\/\/extensions<\/code>, enable developer mode and load the folder. Five steps, explained on the website.<\/p>\n<h2>What we learned from Manifest V3<\/h2>\n<p>Building an extension &#8220;like the old one&#8221; is no longer possible: Manifest V3 removed persistent background pages. In their place there is a service worker that wakes up, batches what you have typed, waits for you to stop and sends it in one go, with a cache so the same text is never checked twice. And no browser lets you underline inside a <code>textarea<\/code>: where it exists we use the CSS Custom Highlight API; where it does not, a mirror layer placed exactly over the text. None of this is visible. All of it is what makes the visible part work: a wavy line under the wrong word.<\/p>\n<h2>Privacy you can read<\/h2>\n<p>No analytics, no cookies, no trackers. Your text travels over HTTPS only to the server you have chosen in the options, and it is not stored. And because all the code is published under AGPLv3, you do not have to take our word for it: you can read it. That licence also requires anyone who modifies the server and offers it publicly to publish their changes too.<\/p>\n<h2>For developers: the same API<\/h2>\n<p>Any program that already talked to LanguageTool can point at the new server without changing anything else:<\/p>\n<pre><code>curl -X POST https:\/\/corrector.damosenelblanco.com\/v2\/check \n  --data-urlencode \"text=Aixo es una prova\" \n  --data-urlencode \"language=ca-ES\"<\/code><\/pre>\n<p>Variants: <code>ca-ES<\/code>, <code>ca-ES-valencia<\/code>, <code>ca-ES-balear<\/code> and <code>es<\/code>. For heavy use, run your own server: the self-hosting documentation is in the repository.<\/p>\n<h2>A small project, on purpose<\/h2>\n<p>Let us be clear: since May 2026 a few dozen people use it. There is no campaign, there are no growth metrics and manual installation puts some people off. <b>We did not build it to win market share. We built it so that it exists<\/b>, and so that if another basic tool changes its business model tomorrow, the answer is already written: the engine was free, the rules belonged to the community, and the work of joining them back together is done and published.<\/p>\n<div class=\"deb-figwrap\"><figure class=\"deb-fig deb-fig--table\" data-deb-chart><figcaption class=\"deb-fig__head\"><span class=\"deb-fig__title\">Three ways to check Catalan in 2026<\/span><\/figcaption>\t\t<div class=\"deb-fig__tscroll\">\n\t\t\t<table class=\"deb-fig__cmp\">\n\t\t\t\t<thead><tr><th><\/th><th>Corrector Catal\u00e0<\/th><th>LanguageTool (official extension)<\/th><th>Softcatal\u00e0 (web checker)<\/th><\/tr><\/thead>\n\t\t\t\t<tbody>\n\t\t\t\t\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t<th scope=\"row\">Price<\/th>\n\t\t\t\t\t\t<td>Free and libre (AGPLv3)<\/td><td>Limited free tier; full checking is Premium<\/td><td>Free<\/td>\t\t\t\t\t<\/tr>\n\t\t\t\t\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t<th scope=\"row\">Checks as you type, on any website<\/th>\n\t\t\t\t\t\t<td><span class=\"deb-fig__mk deb-fig__mk--y\"><i aria-hidden=\"true\">\u2713<\/i>Yes<\/span><\/td><td><span class=\"deb-fig__mk deb-fig__mk--y\"><i aria-hidden=\"true\">\u2713<\/i>Yes<\/span><span class=\"deb-fig__mk-t\">Premium needed to see every error<\/span><\/td><td><span class=\"deb-fig__mk deb-fig__mk--n\"><i aria-hidden=\"true\">\u2717<\/i>No<\/span><span class=\"deb-fig__mk-t\">You paste the text into the website<\/span><\/td>\t\t\t\t\t<\/tr>\n\t\t\t\t\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t<th scope=\"row\">Catalan variants<\/th>\n\t\t\t\t\t\t<td><span class=\"deb-fig__mk deb-fig__mk--y\"><i aria-hidden=\"true\">\u2713<\/i>Yes<\/span><span class=\"deb-fig__mk-t\">Standard, Valencian and Balearic, auto-detected<\/span><\/td><td><span class=\"deb-fig__mk deb-fig__mk--y\"><i aria-hidden=\"true\">\u2713<\/i>Yes<\/span><\/td><td><span class=\"deb-fig__mk deb-fig__mk--y\"><i aria-hidden=\"true\">\u2713<\/i>Yes<\/span><span class=\"deb-fig__mk-t\">Standard, Valencian and Balearic<\/span><\/td>\t\t\t\t\t<\/tr>\n\t\t\t\t\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t<th scope=\"row\">Spanish<\/th>\n\t\t\t\t\t\t<td><span class=\"deb-fig__mk deb-fig__mk--y\"><i aria-hidden=\"true\">\u2713<\/i>Yes<\/span><span class=\"deb-fig__mk-t\">Auto-detected<\/span><\/td><td><span class=\"deb-fig__mk deb-fig__mk--y\"><i aria-hidden=\"true\">\u2713<\/i>Yes<\/span><span class=\"deb-fig__mk-t\">30+ languages<\/span><\/td><td><span class=\"deb-fig__mk deb-fig__mk--n\"><i aria-hidden=\"true\">\u2717<\/i>No<\/span><\/td>\t\t\t\t\t<\/tr>\n\t\t\t\t\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t<th scope=\"row\">Own server (self-hostable)<\/th>\n\t\t\t\t\t\t<td><span class=\"deb-fig__mk deb-fig__mk--y\"><i aria-hidden=\"true\">\u2713<\/i>Yes<\/span><span class=\"deb-fig__mk-t\">Docker + nginx, in 5 minutes<\/span><\/td><td><span class=\"deb-fig__mk deb-fig__mk--n\"><i aria-hidden=\"true\">\u2717<\/i>No<\/span><\/td><td><span class=\"deb-fig__mk deb-fig__mk--n\"><i aria-hidden=\"true\">\u2717<\/i>No<\/span><\/td>\t\t\t\t\t<\/tr>\n\t\t\t\t\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t<th scope=\"row\">Analytics and trackers<\/th>\n\t\t\t\t\t\t<td><span class=\"deb-fig__mk deb-fig__mk--y\"><i aria-hidden=\"true\">\u2713<\/i>Yes<\/span><span class=\"deb-fig__mk-t\">None: the text only goes to the server you choose<\/span><\/td><td><span class=\"deb-fig__mk deb-fig__mk--p\"><i aria-hidden=\"true\">~<\/i>Partly<\/span><span class=\"deb-fig__mk-t\">User account and cloud service<\/span><\/td><td><span class=\"deb-fig__mk deb-fig__mk--p\"><i aria-hidden=\"true\">~<\/i>Partly<\/span><span class=\"deb-fig__mk-t\">Third-party web service<\/span><\/td>\t\t\t\t\t<\/tr>\n\t\t\t\t\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t<th scope=\"row\">Open source<\/th>\n\t\t\t\t\t\t<td><span class=\"deb-fig__mk deb-fig__mk--y\"><i aria-hidden=\"true\">\u2713<\/i>Yes<\/span><span class=\"deb-fig__mk-t\">Extension and server, AGPLv3<\/span><\/td><td><span class=\"deb-fig__mk deb-fig__mk--p\"><i aria-hidden=\"true\">~<\/i>Partly<\/span><span class=\"deb-fig__mk-t\">LGPL engine; proprietary extension<\/span><\/td><td><span class=\"deb-fig__mk deb-fig__mk--p\"><i aria-hidden=\"true\">~<\/i>Partly<\/span><span class=\"deb-fig__mk-t\">LGPL engine<\/span><\/td>\t\t\t\t\t<\/tr>\n\t\t\t\t\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t<th scope=\"row\">Browsers<\/th>\n\t\t\t\t\t\t<td>Chrome, Edge, Brave, Vivaldi \u00b7 Firefox in progress<\/td><td>Chrome, Edge, Firefox, Safari, Opera<\/td><td>Any (it is a website)<\/td>\t\t\t\t\t<\/tr>\n\t\t\t\t\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t<th scope=\"row\">LanguageTool-compatible API<\/th>\n\t\t\t\t\t\t<td><span class=\"deb-fig__mk deb-fig__mk--y\"><i aria-hidden=\"true\">\u2713<\/i>Yes<\/span><span class=\"deb-fig__mk-t\">v2\/check \u00b7 30 requests\/min per IP<\/span><\/td><td><span class=\"deb-fig__mk deb-fig__mk--p\"><i aria-hidden=\"true\">~<\/i>Partly<\/span><span class=\"deb-fig__mk-t\">Paid for heavy use<\/span><\/td><td><span class=\"deb-fig__mk deb-fig__mk--n\"><i aria-hidden=\"true\">\u2717<\/i>No<\/span><\/td>\t\t\t\t\t<\/tr>\n\t\t\t\t\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t<th scope=\"row\">Synced personal dictionary<\/th>\n\t\t\t\t\t\t<td><span class=\"deb-fig__mk deb-fig__mk--y\"><i aria-hidden=\"true\">\u2713<\/i>Yes<\/span><\/td><td><span class=\"deb-fig__mk deb-fig__mk--y\"><i aria-hidden=\"true\">\u2713<\/i>Yes<\/span><span class=\"deb-fig__mk-t\">With an account<\/span><\/td><td><span class=\"deb-fig__mk deb-fig__mk--n\"><i aria-hidden=\"true\">\u2717<\/i>No<\/span><\/td>\t\t\t\t\t<\/tr>\n\t\t\t\t\t\t\t\t\t<\/tbody>\n\t\t\t<\/table>\n\t\t<\/div>\n\t\t<p class=\"deb-fig__source\">Data from each service&#039;s public pages as of 3 September 2026. Softcatal\u00e0 also links to the official LanguageTool extensions.<\/p>\n\t<\/figure>\n\t<\/div>\n<p><a href=\"https:\/\/corrector.damosenelblanco.com\/\" target=\"_blank\" rel=\"noopener\">Try Corrector Catal\u00e0 \u2192<\/a> \u00b7 <a href=\"https:\/\/github.com\/humbertblanco\/correctorcatala\" target=\"_blank\" rel=\"noopener\">Code on GitHub \u2192<\/a><\/p>\n<div class=\"deb-art__extra\">\n<h2>If this interests you<\/h2>\n<p>The checker is one of the studio&#8217;s own brands, like <a href=\"https:\/\/damosenelblanco.com\/en\/proyecto\/quivoto-en\/\">quivoto<\/a> or <a href=\"https:\/\/damosenelblanco.com\/en\/proyecto\/eclipsi-info\/\">eclipsi.info<\/a>: things we build because they were needed and nobody was making them. If your business depends on a tool that could change its price tomorrow, that is a conversation we have often in our <a href=\"https:\/\/damosenelblanco.com\/en\/servicio\/tech-solutions\/\">technology projects<\/a>.<\/p>\n<p><b>Keep going:<\/b> <a href=\"https:\/\/damosenelblanco.com\/en\/proyecto\/corrector-catala-en\/\">the Corrector Catal\u00e0 case study<\/a> \u00b7 <a href=\"https:\/\/damosenelblanco.com\/en\/proyecto\/quivoto-en\/\">quivoto, the compass that starts with your municipality<\/a> \u00b7 <a href=\"https:\/\/damosenelblanco.com\/en\/contact\/\">coffee?<\/a><\/p>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>In January 2025 LanguageTool put its checker behind a paywall. The engine was still free software, so we put the pieces back together: our own server, a Chrome extension and zero trackers.<\/p>\n","protected":false},"author":0,"featured_media":2222,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[71],"tags":[],"class_list":["post-2224","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-technology-en"],"acf":[],"_links":{"self":[{"href":"https:\/\/damosenelblanco.com\/en\/wp-json\/wp\/v2\/posts\/2224","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/damosenelblanco.com\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/damosenelblanco.com\/en\/wp-json\/wp\/v2\/types\/post"}],"replies":[{"embeddable":true,"href":"https:\/\/damosenelblanco.com\/en\/wp-json\/wp\/v2\/comments?post=2224"}],"version-history":[{"count":0,"href":"https:\/\/damosenelblanco.com\/en\/wp-json\/wp\/v2\/posts\/2224\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/damosenelblanco.com\/en\/wp-json\/wp\/v2\/media\/2222"}],"wp:attachment":[{"href":"https:\/\/damosenelblanco.com\/en\/wp-json\/wp\/v2\/media?parent=2224"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/damosenelblanco.com\/en\/wp-json\/wp\/v2\/categories?post=2224"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/damosenelblanco.com\/en\/wp-json\/wp\/v2\/tags?post=2224"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}