Internationalization
PrayCalc supports 8 languages. Switch languages via Settings — the URL stays the same, so prayer time links remain shareable across locales.
Supported languages
| Code | Language | Script | Direction |
|---|---|---|---|
en | English | Latin | LTR |
ar | العربية (Arabic) | Arabic | RTL |
tr | Türkçe (Turkish) | Latin | LTR |
ur | اردو (Urdu) | Nastaliq | RTL |
id | Bahasa Indonesia | Latin | LTR |
fr | Français (French) | Latin | LTR |
bn | বাংলা (Bengali) | Bengali | LTR |
so | Soomaali (Somali) | Latin | LTR |
The default locale is English (en). All prayer names are fully translated including Arabic script equivalents.
RTL layout
For Arabic and Urdu, PrayCalc switches to right-to-left layout automatically. The <html> element receives dir="rtl" when the active locale is ar or ur.
<span><span style="color: var(--shiki-color-text)"><</span><span style="color: var(--shiki-token-string-expression)">html</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">lang</span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)">{locale}</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">dir</span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)">{locale </span><span style="color: var(--shiki-token-keyword)">===</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">'ar'</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">||</span><span style="color: var(--shiki-color-text)"> locale </span><span style="color: var(--shiki-token-keyword)">===</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">'ur'</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">?</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">'rtl'</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">'ltr'</span><span style="color: var(--shiki-color-text)">}</span></span>
<span><span style="color: var(--shiki-color-text)">></span></span>
<span></span>
Tailwind CSS provides first-class RTL support via logical properties (ms-*, me-*, ps-*, pe-*). Components that use these utilities flip correctly without extra CSS.
The Arabic prayer names are sourced directly from Islamic tradition:
| Prayer | Arabic | Transliteration |
|---|---|---|
| Fajr | الفجر | al-Fajr |
| Sunrise | الشروق | ash-Shurūq |
| Dhuhr | الظهر | adh-Dhuhr |
| Asr | العصر | al-ʿAsr |
| Maghrib | المغرب | al-Maghrib |
| Isha | العشاء | al-ʿIshāʾ |
| Qiyam | قيام الليل | Qiyām al-Layl |
Locale detection
PrayCalc uses next-intl with localePrefix: "never" — locale is never added to URLs. Prayer time URLs remain clean:
<span><span style="color: undefined">https://praycalc.com/us/alabama/birmingham</span></span>
<span><span style="color: undefined"> ↑ not /en/us/alabama/birmingham</span></span>
<span><span style="color: undefined"></span></span>
Locale detection order:
NEXT_LOCALEcookie (set when user selects a language in Settings)Accept-LanguageHTTP header (browser preference)- Default:
en
When a user changes their language in Settings, the preference is saved as a cookie and persists across visits.
Translation files
Translation strings live in messages/{locale}.json at the root of praycalc/web. Each file follows the same structure:
<span><span style="color: var(--shiki-color-text)">{</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">"prayers"</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-color-text)"> {</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">"Fajr"</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">"Fajr"</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">"Sunrise"</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">"Sunrise"</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">"Dhuhr"</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">"Dhuhr"</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">"Asr"</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">"Asr"</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">"Maghrib"</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">"Maghrib"</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">"Isha"</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">"Isha"</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">"Qiyam"</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">"Qiyam"</span></span>
<span><span style="color: var(--shiki-color-text)"> }</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">"ui"</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-color-text)"> {</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">"prayerTimesIn"</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">"Prayer Times in {city}"</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">"settings"</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">"Settings"</span></span>
<span><span style="color: var(--shiki-color-text)"> }</span></span>
<span><span style="color: var(--shiki-color-text)">}</span></span>
<span></span>
String interpolation uses {variable} syntax. In components, strings are accessed via:
<span><span style="color: var(--shiki-token-comment)">// Client component</span></span>
<span><span style="color: var(--shiki-token-keyword)">import</span><span style="color: var(--shiki-color-text)"> { useTranslations } </span><span style="color: var(--shiki-token-keyword)">from</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">'next-intl'</span><span style="color: var(--shiki-color-text)">;</span></span>
<span><span style="color: var(--shiki-token-keyword)">const</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">t</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">useTranslations</span><span style="color: var(--shiki-color-text)">(</span><span style="color: var(--shiki-token-string-expression)">'prayers'</span><span style="color: var(--shiki-color-text)">);</span></span>
<span><span style="color: var(--shiki-token-comment)">// t('Fajr') → "الفجر" in Arabic</span></span>
<span></span>
<span><span style="color: var(--shiki-token-comment)">// Server component or layout</span></span>
<span><span style="color: var(--shiki-token-keyword)">import</span><span style="color: var(--shiki-color-text)"> { getTranslations } </span><span style="color: var(--shiki-token-keyword)">from</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">'next-intl/server'</span><span style="color: var(--shiki-color-text)">;</span></span>
<span><span style="color: var(--shiki-token-keyword)">const</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">t</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">await</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">getTranslations</span><span style="color: var(--shiki-color-text)">(</span><span style="color: var(--shiki-token-string-expression)">'ui'</span><span style="color: var(--shiki-color-text)">);</span></span>
<span><span style="color: var(--shiki-token-comment)">// t('prayerTimesIn', { city: 'Mecca' }) → "مواقيت الصلاة في مكة المكرمة"</span></span>
<span></span>
Adding a language
- Create
messages/{locale}.json— copymessages/en.jsonand translate all values - Add the locale code to
i18n/routing.ts:<span><span style="color: var(--shiki-color-text)">locales</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-color-text)"> [</span><span style="color: var(--shiki-token-string-expression)">'en'</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">'ar'</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">'tr'</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">'ur'</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">'id'</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">'fr'</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">'bn'</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">'so'</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">'new-locale'</span><span style="color: var(--shiki-color-text)">]</span><span style="color: var(--shiki-token-punctuation)">,</span></span> <span></span> - Add the language name to each existing
messages/*.jsonunder the"language"key - If RTL: add the locale code to the
dircheck inapp/layout.tsx - Submit a pull request — community translations welcome