[{"data":1,"prerenderedAt":51},["ShallowReactive",2],{"insight-en-multilingual-rtl-website-nuxt":3},{"html":4,"toc":5,"faq":33,"faqTitle":49,"words":50},"\u003Cp>The site you are reading runs in English, Persian and Arabic from one Nuxt codebase. Two of those languages read right to left, use different digits, and one of them usually shows dates in a different calendar. Getting that right is less about translation and more about a dozen small engineering decisions, most of which are invisible when they are correct and painful when they are not.\u003C/p>\n\u003Cp>This is how we build multilingual, right-to-left sites with Nuxt, including the SEO details that decide whether Google treats your language versions as one site or as competing duplicates.\u003C/p>\n\u003Ch2 id=\"section-1\">Routing: one URL per language\u003C/h2>\n\u003Cp>Every page needs a distinct, crawlable URL in every language. The cleanest scheme for most sites is a locale prefix, with the default language at the root:\u003C/p>\n\u003Cul>\n\u003Cli>\u003Ccode>https://asan.team/services\u003C/code> in English\u003C/li>\n\u003Cli>\u003Ccode>https://asan.team/fa/services\u003C/code> in Persian\u003C/li>\n\u003Cli>\u003Ccode>https://asan.team/ar/services\u003C/code> in Arabic\u003C/li>\n\u003C/ul>\n\u003Cp>With \u003Ccode>@nuxtjs/i18n\u003C/code> that is the \u003Ccode>prefix_except_default\u003C/code> strategy:\u003C/p>\n\u003Cpre>\u003Ccode class=\"language-ts\">// nuxt.config.ts\nexport default defineNuxtConfig({\n  modules: [&#39;@nuxtjs/i18n&#39;],\n  i18n: {\n    locales: [\n      { code: &#39;en&#39;, language: &#39;en-US&#39;, file: &#39;en.json&#39;, dir: &#39;ltr&#39; },\n      { code: &#39;fa&#39;, language: &#39;fa-IR&#39;, file: &#39;fa.json&#39;, dir: &#39;rtl&#39; },\n      { code: &#39;ar&#39;, language: &#39;ar-SA&#39;, file: &#39;ar.json&#39;, dir: &#39;rtl&#39; },\n    ],\n    defaultLocale: &#39;en&#39;,\n    strategy: &#39;prefix_except_default&#39;,\n    baseUrl: &#39;https://asan.team&#39;,\n    langDir: &#39;locales/&#39;,\n  },\n})\n\u003C/code>\u003C/pre>\n\u003Cp>Two settings matter more than they look. \u003Ccode>baseUrl\u003C/code> must be the absolute origin, because canonical and hreflang URLs are useless as relative paths. Declaring \u003Ccode>dir\u003C/code> on each locale means direction comes from configuration rather than from an \u003Ccode>if (locale === &#39;fa&#39;)\u003C/code> check somewhere in a component. That check is exactly how Arabic ends up rendering left to right when someone adds it later.\u003C/p>\n\u003Ch2 id=\"section-2\">Direction and language on the html element\u003C/h2>\n\u003Cp>The \u003Ccode>lang\u003C/code> and \u003Ccode>dir\u003C/code> attributes belong on \u003Ccode>&lt;html&gt;\u003C/code>, set on the server so the first paint is already right:\u003C/p>\n\u003Cpre>\u003Ccode class=\"language-ts\">// app.vue\nconst { locale, localeProperties } = useI18n()\nconst direction = computed(() =&gt; localeProperties.value.dir ?? &#39;ltr&#39;)\nconst i18nHead = useLocaleHead({ addSeoAttributes: true })\n\nuseHead({\n  htmlAttrs: { lang: locale, dir: direction },\n  link: computed(() =&gt; i18nHead.value.link ?? []),\n  meta: computed(() =&gt; i18nHead.value.meta ?? []),\n})\n\u003C/code>\u003C/pre>\n\u003Cp>\u003Ccode>useLocaleHead\u003C/code> also generates the canonical link, the hreflang alternates and the \u003Ccode>og:locale\u003C/code> tags for every page. That covers most of the SEO wiring in one call.\u003C/p>\n\u003Ch2 id=\"section-3\">hreflang: telling Google the pages are translations\u003C/h2>\n\u003Cp>Without hreflang, Google sees \u003Ccode>/services\u003C/code>, \u003Ccode>/fa/services\u003C/code> and \u003Ccode>/ar/services\u003C/code> as three separate pages with overlapping topics, and may pick one to show everywhere. With hreflang, each page declares its siblings:\u003C/p>\n\u003Cpre>\u003Ccode class=\"language-html\">&lt;link rel=&quot;alternate&quot; hreflang=&quot;en&quot; href=&quot;https://asan.team/services&quot;&gt;\n&lt;link rel=&quot;alternate&quot; hreflang=&quot;fa&quot; href=&quot;https://asan.team/fa/services&quot;&gt;\n&lt;link rel=&quot;alternate&quot; hreflang=&quot;ar&quot; href=&quot;https://asan.team/ar/services&quot;&gt;\n&lt;link rel=&quot;alternate&quot; hreflang=&quot;x-default&quot; href=&quot;https://asan.team/services&quot;&gt;\n\u003C/code>\u003C/pre>\n\u003Cp>The rules that trip teams up:\u003C/p>\n\u003Cul>\n\u003Cli>\u003Cstrong>Alternates must be reciprocal.\u003C/strong> Every page in the set lists all the others, including itself.\u003C/li>\n\u003Cli>\u003Cstrong>Every alternate must return 200.\u003C/strong> An hreflang pointing at a 404 or a redirect is ignored, and repeated errors make Google trust the rest less.\u003C/li>\n\u003Cli>\u003Cstrong>Add \u003Ccode>x-default\u003C/code>\u003C/strong> for visitors whose language you do not serve.\u003C/li>\n\u003Cli>\u003Cstrong>Repeat the set in the sitemap.\u003C/strong> We generate the sitemap from the prerendered output, so every URL in it exists and carries its alternates.\u003C/li>\n\u003C/ul>\n\u003Cp>\u003Cstrong>Content that exists in only one language\u003C/strong> needs special handling. Our \u003Ca href=\"/insights\">insights\u003C/a> are written separately for each language, not translated, so a Persian article has no English twin. Its page drops the hreflang alternates entirely and keeps only its canonical. The language switcher sends visitors to the insights index in their language rather than to a URL that would 404.\u003C/p>\n\u003Ch2 id=\"section-4\">Do not redirect crawlers by browser language\u003C/h2>\n\u003Cp>A common mistake is redirecting every visitor to the language their browser prefers. Googlebot usually crawls without an \u003Ccode>Accept-Language\u003C/code> header, from US IP addresses. Aggressive redirects can hide your non-English pages from it entirely, and they frustrate users who deliberately chose a language.\u003C/p>\n\u003Cp>Our rule: detect the language only on the root URL, remember the choice in a cookie, and never redirect a URL that already carries a locale prefix. In \u003Ccode>@nuxtjs/i18n\u003C/code> that is \u003Ccode>detectBrowserLanguage\u003C/code> with \u003Ccode>redirectOn: &#39;root&#39;\u003C/code> and \u003Ccode>alwaysRedirect: false\u003C/code>.\u003C/p>\n\u003Ch2 id=\"section-5\">CSS: think in start and end, not left and right\u003C/h2>\n\u003Cp>Right-to-left layout is mostly solved by one habit: \u003Cstrong>use logical properties everywhere.\u003C/strong>\u003C/p>\n\u003Cdiv class=\"prose__table\">\u003Ctable>\u003Cthead>\u003Ctr>\u003Cth>Instead of\u003C/th>\u003Cth>Use\u003C/th>\u003C/tr>\u003C/thead>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Ccode>margin-left\u003C/code>\u003C/td>\u003Ctd>\u003Ccode>margin-inline-start\u003C/code>\u003C/td>\u003C/tr>\n\u003Ctr>\u003Ctd>\u003Ccode>padding-right\u003C/code>\u003C/td>\u003Ctd>\u003Ccode>padding-inline-end\u003C/code>\u003C/td>\u003C/tr>\n\u003Ctr>\u003Ctd>\u003Ccode>left: 0\u003C/code>\u003C/td>\u003Ctd>\u003Ccode>inset-inline-start: 0\u003C/code>\u003C/td>\u003C/tr>\n\u003Ctr>\u003Ctd>\u003Ccode>text-align: left\u003C/code>\u003C/td>\u003Ctd>\u003Ccode>text-align: start\u003C/code>\u003C/td>\u003C/tr>\n\u003Ctr>\u003Ctd>\u003Ccode>border-left\u003C/code>\u003C/td>\u003Ctd>\u003Ccode>border-inline-start\u003C/code>\u003C/td>\u003C/tr>\u003C/tbody>\u003C/table>\u003C/div>\n\u003Cp>Flexbox and grid already follow the document direction. If the codebase uses logical properties from day one, most of the RTL layout comes for free. Retrofitting a finished LTR site is where the real cost is.\u003C/p>\n\u003Cp>A few things logical properties do not cover:\u003C/p>\n\u003Cul>\n\u003Cli>\u003Cstrong>Directional icons.\u003C/strong> Arrows and chevrons that mean &quot;next&quot; or &quot;back&quot; must mirror. We mark them with a \u003Ccode>flip-rtl\u003C/code> class and apply \u003Ccode>transform: scaleX(-1)\u003C/code> under \u003Ccode>html[dir=&#39;rtl&#39;]\u003C/code>. Icons that are not directional, like a search glass or a check mark, stay as they are.\u003C/li>\n\u003Cli>\u003Cstrong>Transforms and animations.\u003C/strong> \u003Ccode>translateX(4px)\u003C/code> moves right in both directions, so hover nudges need an RTL counterpart.\u003C/li>\n\u003Cli>\u003Cstrong>Mixed-direction content.\u003C/strong> Email addresses, phone numbers, code and Latin product names inside Persian text need \u003Ccode>dir=&quot;ltr&quot;\u003C/code> or \u003Ccode>unicode-bidi: isolate\u003C/code>, or their punctuation ends up on the wrong side.\u003C/li>\n\u003C/ul>\n\u003Ch2 id=\"section-6\">Typography for Persian and Arabic\u003C/h2>\n\u003Cul>\n\u003Cli>\u003Cstrong>Never letter-space Arabic script.\u003C/strong> Persian and Arabic letters join. Adding \u003Ccode>letter-spacing\u003C/code> breaks the joins and makes text look broken. The same goes for \u003Ccode>text-transform: uppercase\u003C/code>, which does nothing useful. Wherever our design uses tracked, uppercase labels in English, we reset both for \u003Ccode>fa\u003C/code> and \u003Ccode>ar\u003C/code>.\u003C/li>\n\u003Cli>\u003Cstrong>Choose fonts designed for the script.\u003C/strong> We use Vazirmatn for Persian and Arabic body text and Estedad for Persian display type, both loaded with \u003Ccode>font-display: swap\u003C/code>. Latin fonts with bolted-on Arabic glyphs rarely read well.\u003C/li>\n\u003Cli>\u003Cstrong>Increase line height.\u003C/strong> Arabic script has taller ascenders and deeper descenders. Headings that look right at 1.1 line height in English need around 1.4 in Persian.\u003C/li>\n\u003C/ul>\n\u003Ch2 id=\"section-7\">Numbers and dates\u003C/h2>\n\u003Cp>\u003Ccode>Intl\u003C/code> handles most of it with no library:\u003C/p>\n\u003Cpre>\u003Ccode class=\"language-ts\">new Intl.NumberFormat(&#39;fa-IR&#39;).format(1405)       // &quot;۱٬۴۰۵&quot;\nnew Intl.DateTimeFormat(&#39;fa-IR-u-ca-persian&#39;, { dateStyle: &#39;long&#39; })\n  .format(new Date(&#39;2026-09-27&#39;))                  // &quot;۵ مهر ۱۴۰۵&quot;\nnew Intl.DateTimeFormat(&#39;ar-u-ca-gregory&#39;, { dateStyle: &#39;long&#39; })\n  .format(new Date(&#39;2026-09-27&#39;))                  // a Gregorian date in Arabic\n\u003C/code>\u003C/pre>\n\u003Cp>Persian readers expect the Solar Hijri calendar, which \u003Ccode>-u-ca-persian\u003C/code> provides. Saudi locales default to the Islamic calendar, so set the calendar explicitly when you mean Gregorian. For machine-readable output such as \u003Ccode>&lt;time datetime&gt;\u003C/code> and structured data, always use ISO dates.\u003C/p>\n\u003Cp>\u003Cstrong>Normalize input as well as output.\u003C/strong> Users type Persian, Arabic and Latin digits interchangeably, and Persian text often contains the Arabic forms of &quot;ی&quot; and &quot;ک&quot;. Normalize both before validating a phone number or running a search, or identical queries will miss each other.\u003C/p>\n\u003Ch2 id=\"section-8\">Per-language SEO, not translated SEO\u003C/h2>\n\u003Cp>Translation is not localization, and it is definitely not SEO. For each language:\u003C/p>\n\u003Cul>\n\u003Cli>\u003Cstrong>Research keywords in that language.\u003C/strong> Persian searchers do not phrase queries as translations of English ones. Our Persian service pages target different terms than the English ones, not translated terms.\u003C/li>\n\u003Cli>\u003Cstrong>Write titles and descriptions per language.\u003C/strong> Each locale file carries its own \u003Ccode>seo\u003C/code> block.\u003C/li>\n\u003Cli>\u003Cstrong>Set \u003Ccode>og:locale\u003C/code> correctly.\u003C/strong> Use \u003Ccode>fa_IR\u003C/code> and \u003Ccode>ar_AR\u003C/code>. A missing mapping silently falls back to \u003Ccode>en_US\u003C/code>.\u003C/li>\n\u003Cli>\u003Cstrong>Keep structured data in the page&#39;s language.\u003C/strong> Names, descriptions and FAQ answers in JSON-LD should match the visible text of that locale.\u003C/li>\n\u003C/ul>\n\u003Ch2 id=\"section-9\">A checklist before launch\u003C/h2>\n\u003Col>\n\u003Cli>\u003Ccode>lang\u003C/code> and \u003Ccode>dir\u003C/code> on \u003Ccode>&lt;html&gt;\u003C/code>, rendered on the server.\u003C/li>\n\u003Cli>Canonical plus reciprocal hreflang with \u003Ccode>x-default\u003C/code> on every page.\u003C/li>\n\u003Cli>No hreflang pointing at URLs that 404 or redirect.\u003C/li>\n\u003Cli>A sitemap generated from real output, with alternates.\u003C/li>\n\u003Cli>Logical CSS properties throughout, and mirrored directional icons.\u003C/li>\n\u003Cli>No letter-spacing or uppercase transforms on Arabic-script text.\u003C/li>\n\u003Cli>Script-appropriate fonts and larger line heights.\u003C/li>\n\u003Cli>Locale-aware numbers and dates, and normalized input.\u003C/li>\n\u003Cli>Keyword research, titles and descriptions written per language.\u003C/li>\n\u003Cli>Each language checked in Search Console after launch.\u003C/li>\n\u003C/ol>\n\u003Cp>If you are planning a multilingual or right-to-left product, our \u003Ca href=\"/services/frontend-development\">frontend development\u003C/a> and \u003Ca href=\"/services/seo-content\">SEO\u003C/a> pages describe how we work, and this site is a live example of the result.\u003C/p>\n",[6,9,12,15,18,21,24,27,30],{"id":7,"text":8},"section-1","Routing: one URL per language",{"id":10,"text":11},"section-2","Direction and language on the html element",{"id":13,"text":14},"section-3","hreflang: telling Google the pages are translations",{"id":16,"text":17},"section-4","Do not redirect crawlers by browser language",{"id":19,"text":20},"section-5","CSS: think in start and end, not left and right",{"id":22,"text":23},"section-6","Typography for Persian and Arabic",{"id":25,"text":26},"section-7","Numbers and dates",{"id":28,"text":29},"section-8","Per-language SEO, not translated SEO",{"id":31,"text":32},"section-9","A checklist before launch",[34,37,40,43,46],{"q":35,"a":36},"Should each language have its own URL?","Yes. Search engines index URLs, not languages. If languages share one URL and switch by cookie or browser setting, only one version will ever be indexed. Use a prefix such as /fa/, a subdomain, or a separate domain.",{"q":38,"a":39},"Is a subdirectory or a subdomain better for languages?","For most sites, subdirectories like /fa/ are simpler and keep all languages under one domain's authority. Separate country domains make sense when you run distinct businesses per market.",{"q":41,"a":42},"How do I make a Nuxt site right-to-left?","Declare dir: 'rtl' on the locale in @nuxtjs/i18n, set the dir attribute on the html element from the active locale, and write CSS with logical properties such as margin-inline-start so layouts mirror automatically.",{"q":44,"a":45},"Do I need hreflang if my languages target different countries?","Yes. hreflang tells search engines which version to show to which audience and stops the versions from competing with each other. Add x-default for everyone else.",{"q":47,"a":48},"How should Persian dates be displayed?","Most Persian readers expect the Solar Hijri calendar. In JavaScript, Intl.DateTimeFormat with the fa-IR-u-ca-persian locale produces it without a library. Keep ISO dates in markup and structured data.","Frequently asked questions",1399,1790485335203]