惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

F
Full Disclosure
Recorded Future
Recorded Future
T
Tenable Blog
S
Securelist
C
CERT Recently Published Vulnerability Notes
T
Threatpost
S
Schneier on Security
A
Arctic Wolf
The Hacker News
The Hacker News
C
CXSECURITY Database RSS Feed - CXSecurity.com
Know Your Adversary
Know Your Adversary
P
Privacy International News Feed
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
The Register - Security
The Register - Security
Cisco Talos Blog
Cisco Talos Blog
AWS News Blog
AWS News Blog
K
Kaspersky official blog
T
True Tiger Recordings
T
Threat Research - Cisco Blogs
V
Vulnerabilities – Threatpost
P
Palo Alto Networks Blog
T
The Exploit Database - CXSecurity.com
小众软件
小众软件
B
Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Microsoft Azure Blog
Microsoft Azure Blog
Cyberwarzone
Cyberwarzone
C
Cybersecurity and Infrastructure Security Agency CISA
T
Tor Project blog
Spread Privacy
Spread Privacy
Malwarebytes
Malwarebytes
P
Proofpoint News Feed
F
Fox-IT International blog
F
Fortinet All Blogs
P
Privacy & Cybersecurity Law Blog
G
GRAHAM CLULEY
量子位
Latest news
Latest news
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
博客园 - 叶小钗
Project Zero
Project Zero
T
Tailwind CSS Blog
N
Netflix TechBlog - Medium
Martin Fowler
Martin Fowler
IntelliJ IDEA : IntelliJ IDEA – the Leading IDE for Professional Development in Java and Kotlin | The JetBrains Blog
IntelliJ IDEA : IntelliJ IDEA – the Leading IDE for Professional Development in Java and Kotlin | The JetBrains Blog
I
Intezer
博客园_首页
腾讯CDC
H
Hackread – Cybersecurity News, Data Breaches, AI and More
D
Darknet – Hacking Tools, Hacker News & Cyber Security

DEV Community

Are we still in the Console Era of AI? Building a Senior-Level DevOps / SRE / Infrastructure Engineer Terminal Setup (macOS) Media Queries, Transitions, Positions, and Units (rem vs em) Explained Vibe Coding Will Destroy Your Software Engineering Career Your Payment API Wasn't Built for AI Agents. Open Banking Might Be the Fix. The Amazon Interview Process in 2026: Every Round Decoded (With Copy-Paste Scripts) Why Most Social Platforms Optimize Engagement Instead of Emotional Safety How to Build Your Own AI API Gateway (70x Cheaper Than GPT-4o) OpenBrief Review: Local-First Video AI Summarizer 2026 Announcing LightningChart JS Trader v.4.1 TensorCircuit-NG: Quantum Software On AI, For AI, With AI Open-Source Multi-Agent Orchestration: Lessons from AgentForge AI Agents in Practice — Part 3: How the Control Loop Actually Works Polymarket vs Kalshi: Who Actually Wins on Volume and Liquidity I Wired 8 MCP Servers Into One Claude Agent. 3 Pairs Quietly Fought Over the Same Tool Name. Twenty Minutes, Seventeen Organizations DNSControl + CoreDNS Container Example - Announcement Tech Talks Weekly #106 Umka Parental Control CI/CD for Side Projects: 3 Pragmatic Design Choices Why Agentic AI Is the Most Over-Hyped — and Under-Delivering — Trend of 2026 How teams can add a custom LineageLens adapter — a practical, code-free guide What Engineers Learn After Building Enterprise Chatbots That Actually Go Live The case for compiled, typed CSS (blame AI) Your Terraform estate documents itself now: meet iac-cartographer Vector‑native RAG on Oracle: embeddings, HNSW/IVF, and hybrid search under database governance I Stumbled Into a 40x Cost Reduction by Switching to Chinese AI Models China vs US AI Models in 2026: The Architecture Decision That Saves 40x Chinese AI Models Are 40x Cheaper Than GPT-4o — Here's the Proof ERC-8004 Agent Validation: Trustless Reputation for DeFi Bots Claude Managed Agents Outcomes: Auto-Grading Agent Work 5 URL Encoding Bugs That Silently Break Your App Which AI Tool Wins? Wrong Question. API Contract-Driven Development (Build Reliable Systems Without Guesswork) I built 'Ask Your Life' — a personal Coral agent that answers questions about your money & deadlines with SQL 5G RedCap for embedded IoT: useful 5G without full 5G complexity Building a Live Odds Dashboard in React (without the re-render storm) How to Build Token-Efficient Web Scraping Pipelines for AI Agents Using n8n PyLadies Dublin June Meetup The Dangerous Myth of the "10x Developer" (And Who You Actually Want) I Hardened a Rust Media Upload API with Magic Bytes, Atomic Quotas, and Race Condition Fixes (Part 3) The Moment We Realized the Language Was the Constraint in the Veltrix Treasure Hunt Engine ABAC and CASL with NestJS What If AI Fact-Checked Your Meetings in Real Time? Inside Meeting-Time AI Skills Don't Wrap the LLM. Make Its Failure Modes Unreachable. Building Autonomous DeFi Agents on Arbitrum: From Events to Execution The One Cache That Broke Our Treasure Hunt Engine Why your AI chat reconnects but your session doesn't Why I Built Tenurr: A Private Career Ledger and Document Vault for Engineers (And Solved "Career Amnesia") Rate Limiting in C# — Don't Let Your API Get Hammered I audited the 12 fastest-growing new GitHub repos for fake stars. Here's the base rate. I Stopped Treating AI Agents Like Toys After Hermes Agent Started Running My Entire Week SVG Keyframe Animation in Pure CSS (No Library) The Hidden Cost of Fake Invoices: $127,000 Lost Per Incident The Stream class in Dart Kubernetes HPA Scale to Zero Without KEDA: Native Autoscaling for Idle Workloads Building a Gaming Content Platform with Game Pages and News Articles Can Quantum Computing Change AI? A Deep Dive Into Quantum Machine Learning My PC setup as a Linux user Why Your Chart Library Is the Bottleneck You Never Suspected by Andrew Burnett-Thompson, CEO & Founder of SciChart i touched AWS and stuff didn't break (mostly) Using Google's New AI Command-Line Assistant: Antigravity CLI (agy) and YOLO's No-Confirmation Mode GCP: Upgrading a LINE Bot with Vertex AI ADK Tools for Smart Business Cards and Backup Search My Journey into Web3 Auditing Securing AI Generated Code: You Ship It, You Own It Optimizing Browser Fingerprint Spoofing and Session Validation in Automated Scrapers I Scanned a Vulnerable Kubernetes Cluster with 9 Engines — The AI Filter Caught Everything When the Treasure Hunt Engine ate my weekend How to Choose the Right AI Course in Mumbai Building an Interactive Tier List with Next.js — NTE Tier List Case Study Website Accessibility Audit: The Complete Guide (WCAG 2.2) GitHub has had 257 incidents in 12 months. Here's what that means for your CI pipelines The Moment We Realized the Default Config Was a Lie Grafana Pricing Teardown 2026 Infisical Pricing Teardown 2026 Langfuse Pricing Teardown 2026 Metabase Pricing Teardown 2026 n8n Pricing Teardown 2026 Novu Pricing Teardown 2026 Plane Pricing Teardown 2026 Temporal Pricing Teardown 2026 Python 101 a Comprehensive Guide ToolJet Pricing Teardown 2026 Dev.to is such a fantastic platform for developers, writers, and tech enthusiasts to share knowledge and learn from each other. I really appreciate how the community encourages creativity, collaboration, and continuous learning through insightful articles Twenty CRM Pricing Teardown 2026 Ever Wondered What Actually Happens When You Click “Send” on an Email? Automating MongoDB Auditlogs Cleanup & Restore Workflow with S3 Backup Best Java Web Scraping Libraries The padlock doesn't mean what you think it means I built a simple pytest plugin for test observability (need your help 😅) Laravel AI SDK Silently Kills Your Horizon Queue (And How to Fix It in 4 Config Changes) The Day We Hardcoded 42 in the Treasure Hunt Engine Today we are launching on Product Hunt! I built FreeLedger to end the freelance finance nightmare Fintech Devs May Get Fed Master Accounts Karpathy Joined Anthropic to Train Claude Using Claude Just released my new Flutter package: smart_player_kit The Day the Treasure Hunt Engine Decided to Lie to Us About Latency Django Session Cookie vs localStorage JWT Security Comparison The Day Our Treasure Hunt Engine Blew Up at 3 AM How I Built 8 Free Dev Tools as a Solo Maker — Lessons Learned
I love MJML — I just didn't want a whole templating engine for two tiny things
Michal Král · 2026-05-27 · via DEV Community

I love MJML.

Responsive HTML email is one of the genuinely miserable corners of frontend — nested tables, <!--[if mso]> incantations, clients from 2007 that still get a vote. MJML takes all of that and lets me write semantic-ish components (<mj-section>, <mj-column>, <mj-button>) that compile down to email HTML that actually renders everywhere. It's one of those tools that makes a horrible problem boring, which is the highest compliment I can give a library.

So I was happy. Except for one thing.

The one thing I was missing

My templates needed two unglamorous things:

  1. Variables — drop a name, a URL, an order number into the email.
  2. Translations — the same template in en, cs, whatever.

MJML doesn't do either, and fair enough — it's a markup compiler, not a template engine. So like a lot of people, I reached for the template engine I already had and ran my .mjml files through Twig first, just to get this:

<mj-text>{{ 'welcome.hello'|trans({'%name%': name}) }}</mj-text>

Enter fullscreen mode Exit fullscreen mode

And it worked. But look at what that actually is: I'm spinning up an entire templating engine — with loops, conditionals, filters, its own parsing pass — to do string interpolation and a dictionary lookup. Two compile steps (Twig → MJML) for what should be one. A second syntax in my files. And every time I touched a template I had to keep two mental models in sync.

I didn't want a template engine in my email pipeline. I wanted those two tiny things — and nothing else.

What I actually wanted

Co-located translations, vue-i18n style — the strings living right next to the markup that uses them — plus dead-simple variable access. Something like:

<mjml>
  <i18n type="json">
    {
      "en": { "hello": "Hello {name}!", "cta": "Shop now" },
      "cs": { "hello": "Ahoj {name}!", "cta": "Nakupovat" }
    }
  </i18n>

  <mj-body>
    <mj-section>
      <mj-column>
        <mj-text>{{ i18n('hello', { name: get('firstName') }) }}</mj-text>
        <mj-button href="{{ get('url') }}">{{ i18n('cta') }}</mj-button>
      </mj-column>
    </mj-section>
  </mj-body>
</mjml>

Enter fullscreen mode Exit fullscreen mode

No second template language. No second compile. Just i18n() and get().

So I built it: @checkthiscloud/mjml-i18n.

Using it

It's a single MJML preprocessor:

import mjml from 'mjml';
import { createI18nPreprocessor } from '@checkthiscloud/mjml-i18n';

const preprocessor = createI18nPreprocessor({
  locale: 'cs',
  vars: { firstName: 'Ada', url: 'https://example.com' },
});

const { html } = await mjml(template, { preprocessors: [preprocessor] });

Enter fullscreen mode Exit fullscreen mode

That's the whole integration. One preprocessor in, fully resolved email HTML out.

How it works (and what it deliberately doesn't do)

The trick is that it runs as an MJML preprocessor — it resolves the {{ … }} markers on the raw XML, before MJML parses it. Two nice consequences:

  • Markers work anywhere, including inside attributes (href="{{ get('url') }}"), not just text nodes.
  • It composes with MJML instead of fighting it.

Inside each {{ … }} is a single function-call expression, evaluated by a tiny allowlist (built on jsep). Only literals, object arguments, and the registered functions (i18n, get) are allowed. You can nest them — i18n('hello', { name: get('firstName') }) — and that's it.

What it is not is a template engine. No loops, no conditionals, no arbitrary expressions, no eval. If a marker isn't a recognized call, it's left untouched rather than executed. That constraint is the whole point: I wanted the two things I was missing, not a Turing-complete language living in my emails.

Translations fall back gracefully (missing key → the key, missing variable → an obvious placeholder), so a typo degrades instead of exploding mid-render.

Status

It's young — 0.x, MIT, built for MJML v5 — but small and focused, and it does exactly what it says. Params are currently plain {name} substitution (no ICU plurals yet, though the seam is there if that day comes). If you live in MJML and you've been bolting a template engine onto it just for translations, give it a spin and tell me where it breaks.

npm install @checkthiscloud/mjml-i18n mjml

Enter fullscreen mode Exit fullscreen mode

Sometimes the feature you want isn't "more" — it's exactly two small things and nothing else.