{"id":5860,"date":"2026-10-08T01:25:06","date_gmt":"2026-10-08T04:25:06","guid":{"rendered":"https:\/\/tucumandevelopers.com\/index.php\/2026\/10\/08\/building-for-europe-what-nobody-tells-you-about-gdpr-adsense-and-multilingual-next-js-seo\/"},"modified":"2026-10-08T01:25:06","modified_gmt":"2026-10-08T04:25:06","slug":"building-for-europe-what-nobody-tells-you-about-gdpr-adsense-and-multilingual-next-js-seo","status":"publish","type":"post","link":"https:\/\/tucumandevelopers.com\/index.php\/2026\/10\/08\/building-for-europe-what-nobody-tells-you-about-gdpr-adsense-and-multilingual-next-js-seo\/","title":{"rendered":"Building for Europe: What nobody tells you about GDPR, AdSense, and multilingual Next.js SEO"},"content":{"rendered":"<div>\n<div><\/div>\n<p>I had 40+ pages flagged as &#8220;Alternate page with proper <br \/> canonical tag&#8221; in GSC because of this mistake.<\/p>\n<p><strong>## Mistake 4: localePrefix: &#8216;as-needed&#8217; Breaks Things<\/strong><\/p>\n<p>next-intl default is &#8216;as-needed&#8217; which removes \/en\/ <br \/> from English URLs. Sounds clean. Causes redirect loops <br \/> on Cloudflare Workers. <\/p>\n<div>\n<pre><code><span>\/\/ Use this instead:<\/span> <span>export<\/span> <span>const<\/span> <span>routing<\/span> <span>=<\/span> <span>defineRouting<\/span><span>({<\/span> <span>locales<\/span><span>:<\/span> <span>[<\/span><span>'<\/span><span>en<\/span><span>'<\/span><span>,<\/span> <span>'<\/span><span>de<\/span><span>'<\/span><span>,<\/span> <span>'<\/span><span>fr<\/span><span>'<\/span><span>,<\/span> <span>'<\/span><span>es<\/span><span>'<\/span><span>],<\/span> <span>defaultLocale<\/span><span>:<\/span> <span>'<\/span><span>en<\/span><span>'<\/span><span>,<\/span> <span>localePrefix<\/span><span>:<\/span> <span>'<\/span><span>always<\/span><span>'<\/span> <span>\/\/ keeps \/en\/ in URL<\/span> <span>})<\/span> <\/code><\/pre>\n<div>\n<\/p><\/div>\n<\/p><\/div>\n<p>GSC flagged \/en and \/en\/tools\/calculator\/vat-calculator <br \/> as redirect errors until I fixed this.<\/p>\n<p><strong>## Mistake 5: Content vs Tools<\/strong><\/p>\n<blockquote>\n<p>Every tool page needs 600+ words of content. Not optional.<\/p>\n<\/blockquote>\n<p>Google doesn&#8217;t rank interactive widgets. It ranks pages.<br \/> Your tool page IS a content page that embeds a tool.<\/p>\n<p>The highest-traffic pages on OmniWebTool aren&#8217;t the <br \/> tools themselves \u2014 they&#8217;re the explanation pages that <br \/> surround them.<\/p>\n<p><strong>&#8220;Wie rechnet man die Mehrwertsteuer aus?&#8221; <br \/> (How do you calculate VAT?)<\/strong> \u2014 people search this <br \/> question, not just the calculator.<\/p>\n<p>Build the content. The tool is the CTA.<\/p>\n<p><strong>## Mistake 6: Cloudflare Workers + Next.js 14 Runtime<\/strong><\/p>\n<p><strong>This cost me 6 hours of debugging.<\/strong> \ud83d\ude2d <\/p>\n<div>\n<pre><code><span>\/\/ middleware.ts \u2014 DO NOT declare runtime<\/span> <span>\/\/ Next.js 14 middleware is edge by default<\/span> <span>\/\/ This line BREAKS the build:<\/span> <span>export<\/span> <span>const<\/span> <span>runtime<\/span> <span>=<\/span> <span>'<\/span><span>edge<\/span><span>'<\/span><span>;<\/span> <span>\/\/ \u274c Remove this<\/span> <span>\/\/ Page components \u2014 use experimental-edge, NOT edge<\/span> <span>export<\/span> <span>const<\/span> <span>runtime<\/span> <span>=<\/span> <span>'<\/span><span>experimental-edge<\/span><span>'<\/span><span>;<\/span> <span>\/\/ \u2705<\/span> <span>\/\/ 'edge' is Next.js 15+ only<\/span> <\/code><\/pre>\n<div>\n<\/p><\/div>\n<\/p><\/div>\n<p>Build command that works: <\/p>\n<div>\n<pre><code>npx opennextjs-cloudflare build <\/code><\/pre>\n<div>\n<\/p><\/div>\n<\/p><\/div>\n<p>Deploy command: <\/p>\n<div>\n<pre><code>npx wrangler deploy <\/code><\/pre>\n<div>\n<\/p><\/div>\n<\/p><\/div>\n<p><strong>## What Actually Moves the Needle in Week 1<\/strong><\/p>\n<p>After 7 days:<\/p>\n<ul>\n<li>954 static pages across 4 languages<\/li>\n<li>87 pages indexed so far<\/li>\n<li>900+ GSC impressions (German keywords first)<\/li>\n<li>Listed on top sites<\/li>\n<\/ul>\n<p>German keywords showed impressions before English ones.<br \/> European users are underserved. Local language content <br \/> ranks faster in local markets.<\/p>\n<p><strong>## The VAT Calculator Insight<\/strong><\/p>\n<p>Built the VAT Calculator first \u2014 covers all EU-27 <br \/> countries with reverse calculation.<\/p>\n<p>&#8220;Mehrwertsteuerrechner&#8221; \u2014 60K searches\/month Germany<br \/> &#8220;Calculateur TVA&#8221; \u2014 40K searches\/month France<br \/> &#8220;Calculadora IVA&#8221; \u2014 35K searches\/month Spain<\/p>\n<p>Finance keywords = $3-12 CPC on AdSense.<br \/> One localized calculator is worth more than <br \/> 30 generic English tools.<\/p>\n<p><strong>## Links<\/strong><\/p>\n<ul>\n<li>Live: omniwebtool.com<\/li>\n<li>Builder: Ovrion (ovrion.xyz)<\/li>\n<li>Previous post: [Cloudflare + Next.js deployment guide]<\/li>\n<\/ul>\n<p>Questions welcome about multilingual SEO, <br \/> GDPR compliance, or Cloudflare Workers setup. Peace \ud83d\ude4c\u270c<\/p>\n<\/p><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>Fuente: <a href=\"https:\/\/dev.to\/ovrion\/building-for-europe-what-nobody-tells-you-about-gdpr-adsense-and-multilingual-nextjs-seo-1inf\">Art\u00edculo original<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>I had 40+ pages flagged as &#8220;Alternate page with proper canonical tag&#8221; in GSC because of this mistake. ## Mistake 4: localePrefix: &#8216;as-needed&#8217; Breaks Things next-intl default is &#8216;as-needed&#8217; which removes \/en\/ from English URLs. Sounds clean. Causes redirect loops on Cloudflare Workers. \/\/ Use this instead: export const routing = defineRouting({ locales: [&#8216;en&#8217;, &#8216;de&#8217;, [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":5859,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2},"webixso_pending_account_ids":""},"categories":[41],"tags":[],"class_list":["post-5860","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-devto"],"jetpack_publicize_connections":[],"_links":{"self":[{"href":"https:\/\/tucumandevelopers.com\/index.php\/wp-json\/wp\/v2\/posts\/5860","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/tucumandevelopers.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/tucumandevelopers.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/tucumandevelopers.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/tucumandevelopers.com\/index.php\/wp-json\/wp\/v2\/comments?post=5860"}],"version-history":[{"count":0,"href":"https:\/\/tucumandevelopers.com\/index.php\/wp-json\/wp\/v2\/posts\/5860\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/tucumandevelopers.com\/index.php\/wp-json\/wp\/v2\/media\/5859"}],"wp:attachment":[{"href":"https:\/\/tucumandevelopers.com\/index.php\/wp-json\/wp\/v2\/media?parent=5860"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/tucumandevelopers.com\/index.php\/wp-json\/wp\/v2\/categories?post=5860"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/tucumandevelopers.com\/index.php\/wp-json\/wp\/v2\/tags?post=5860"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}