Localization & Internationalization
Multi-language support with Persian/Farsi (fa-IR) and RTL capabilities
Overview
The Shirinzad E-Commerce Platform is built with comprehensive internationalization (i18n) support, including full Persian/Farsi language support with RTL (Right-to-Left) text rendering. The platform uses ABP Framework's powerful localization system with 25+ language files.
Key Features: 25 language files, RTL support for Persian and Arabic, culture-specific date/time formatting, currency formatting for Iranian Rial (IRR), and dynamic language switching.
Supported Languages (25 Languages)
Available Language Files
The platform includes 25 language files located in: Shirinzad.Shop.Domain.Shared/Localization/Shop/
| Language | Culture Code | File | RTL Support |
|---|---|---|---|
| English (Default) | en | en.json | No |
| English (UK) | en-GB | en-GB.json | No |
| Arabic | ar | ar.json | Yes |
| Czech | cs | cs.json | No |
| German | de | de.json | No |
| Spanish | es | es.json | No |
| Finnish | fi | fi.json | No |
| French | fr | fr.json | No |
| Hindi | hi | hi.json | No |
| Croatian | hr | hr.json | No |
| Hungarian | hu | hu.json | No |
| Icelandic | is | is.json | No |
| Italian | it | it.json | No |
| Dutch | nl | nl.json | No |
| Polish | pl-PL | pl-PL.json | No |
| Portuguese (Brazil) | pt-BR | pt-BR.json | No |
| Romanian | ro-RO | ro-RO.json | No |
| Russian | ru | ru.json | No |
| Slovak | sk | sk.json | No |
| Slovenian | sl | sl.json | No |
| Swedish | sv | sv.json | No |
| Turkish | tr | tr.json | No |
| Vietnamese | vi | vi.json | No |
| Chinese (Simplified) | zh-Hans | zh-Hans.json | No |
| Chinese (Traditional) | zh-Hant | zh-Hant.json | No |
Note: To add Persian/Farsi (fa-IR) support, create a new
fa.json file in the Localization/Shop directory with Persian translations.
RTL (Right-to-Left) Support
RTL Languages Configuration
The platform includes comprehensive RTL support for Persian/Farsi and Arabic languages using Bootstrap RTL CSS.
RTL Detection and CSS Loading
@{
var currentCulture = CultureInfo.CurrentUICulture.Name;
var isRtl = currentCulture.StartsWith("ar") || currentCulture.StartsWith("fa");
}
@if (isRtl)
{
<link rel="stylesheet" href="~/libs/bootstrap/css/bootstrap.rtl.min.css" />
<link rel="stylesheet" href="~/css/site.rtl.css" />
}
else
{
<link rel="stylesheet" href="~/libs/bootstrap/css/bootstrap.min.css" />
<link rel="stylesheet" href="~/css/site.css" />
}
RTL CSS (site.rtl.css)
/* RTL Layout Adjustments */
body {
direction: rtl;
text-align: right;
}
.sidebar {
right: 0;
left: auto;
}
.main-content {
margin-right: 250px;
margin-left: 0;
}
/* Flip Icons */
.icon-left {
transform: scaleX(-1);
}
/* Form Elements */
.form-control {
text-align: right;
}
/* Tables */
.table th,
.table td {
text-align: right;
}
/* Breadcrumbs */
.breadcrumb-item + .breadcrumb-item::before {
content: "\\";
padding-left: 0.5rem;
padding-right: 0.5rem;
}
/* Dropdown Menus */
.dropdown-menu {
right: 0;
left: auto;
}
Dynamic HTML Direction Attribute
<html lang="@CultureInfo.CurrentUICulture.Name" dir="@(isRtl ? "rtl" : "ltr")">
ABP Localization System
Configuration
ShopDomainSharedModule.cs
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpVirtualFileSystemOptions>(options =>
{
options.FileSets.AddEmbedded<ShopDomainSharedModule>();
});
Configure<AbpLocalizationOptions>(options =>
{
options.Resources
.Add<ShopResource>("en")
.AddBaseTypes(typeof(AbpValidationResource))
.AddVirtualJson("/Localization/Shop");
options.DefaultResourceType = typeof(ShopResource);
});
Configure<AbpExceptionLocalizationOptions>(options =>
{
options.MapCodeNamespace("Shop", typeof(ShopResource));
});
}
Localization Resource (ShopResource.cs)
using Volo.Abp.Localization;
namespace Shirinzad.Shop.Localization;
[LocalizationResourceName("Shop")]
public class ShopResource
{
}
JSON File Structure
English (en.json)
{
"culture": "en",
"texts": {
"AppName": "Shop",
"Menu:Home": "Home",
"Welcome": "Welcome",
"LongWelcomeMessage": "Welcome to the application. This is a startup project based on the ABP framework. For more information, visit abp.io."
}
}
Arabic (ar.json) - RTL Example
{
"culture": "ar",
"texts": {
"AppName": "Shop",
"Menu:Home": "الصفحة الرئيسية",
"Welcome": "مرحباً",
"LongWelcomeMessage": "مرحبا بكم في التطبيق. هذا مشروع بدء تشغيل يعتمد على إطار عمل ABP. لمزيد من المعلومات ، يرجى زيارة abp.io."
}
}
Persian/Farsi (fa.json) - Example Template
{
"culture": "fa",
"texts": {
"AppName": "فروشگاه",
"Menu:Home": "صفحه اصلی",
"Welcome": "خوش آمدید",
"LongWelcomeMessage": "به برنامه خوش آمدید. این یک پروژه راهاندازی است که بر اساس چارچوب ABP ساخته شده است. برای اطلاعات بیشتر، از abp.io دیدن کنید.",
"Menu:Products": "محصولات",
"Menu:Categories": "دستهبندیها",
"Menu:Orders": "سفارشات",
"Menu:Customers": "مشتریان",
"Menu:Reports": "گزارشها",
"Menu:Settings": "تنظیمات",
"Product:Name": "نام محصول",
"Product:Price": "قیمت",
"Product:Stock": "موجودی",
"Product:Description": "توضیحات",
"Product:Category": "دستهبندی",
"Product:Brand": "برند",
"Order:Number": "شماره سفارش",
"Order:Date": "تاریخ",
"Order:Status": "وضعیت",
"Order:Total": "مجموع",
"Order:Customer": "مشتری",
"Button:Save": "ذخیره",
"Button:Cancel": "لغو",
"Button:Delete": "حذف",
"Button:Edit": "ویرایش",
"Button:Add": "افزودن",
"Button:Search": "جستجو",
"Message:Success": "عملیات با موفقیت انجام شد",
"Message:Error": "خطا در انجام عملیات",
"Message:DeleteConfirm": "آیا از حذف این مورد اطمینان دارید؟"
}
}
Using Localization in Code
1. In Application Services (C#)
public class ProductAppService : ApplicationService
{
public ProductAppService()
{
LocalizationResource = typeof(ShopResource);
}
public async Task<ProductDto> CreateAsync(CreateProductDto input)
{
// Use L to localize strings
var successMessage = L["Product:CreatedSuccessfully"];
// With parameters
var welcomeMessage = L["Welcome:User", input.UserName];
return result;
}
}
2. In Razor Pages/Views
@using Microsoft.Extensions.Localization
@using Shirinzad.Shop.Localization
@inject IStringLocalizer<ShopResource> L
<h1>@L["Menu:Home"]</h1>
<p>@L["Welcome"]</p>
<!-- With parameters -->
<p>@L["Welcome:User", Model.UserName]</p>
<!-- In buttons -->
<button>@L["Button:Save"]</button>
<button>@L["Button:Cancel"]</button>
3. In JavaScript/TypeScript
// Using ABP's localization service
abp.localization.getResource('Shop');
// Localize a key
const welcomeMessage = abp.localization.localize('Welcome', 'Shop');
// With parameters
const userMessage = abp.localization.localize('Welcome:User', 'Shop', userName);
// In Swal alerts
Swal.fire({
title: abp.localization.localize('Message:DeleteConfirm', 'Shop'),
icon: 'warning',
showCancelButton: true,
confirmButtonText: abp.localization.localize('Button:Delete', 'Shop'),
cancelButtonText: abp.localization.localize('Button:Cancel', 'Shop')
});
Culture-Specific Formatting
1. Date & Time Formatting
Persian Calendar (Solar Hijri)
// C# - Persian Date Formatting
var persianCalendar = new PersianCalendar();
var persianDate = $"{persianCalendar.GetYear(date)}/{persianCalendar.GetMonth(date)}/{persianCalendar.GetDayOfMonth(date)}";
// Using CultureInfo
var persianCulture = new CultureInfo("fa-IR");
var formattedDate = date.ToString("d", persianCulture); // Short date
var formattedDateTime = date.ToString("G", persianCulture); // General date/time
JavaScript Date Formatting
// Using Luxon for Persian dates
const dt = DateTime.now().setLocale('fa-IR');
const persianDate = dt.toLocaleString(DateTime.DATE_FULL);
// Using moment.js with Persian calendar
moment.locale('fa');
const persianMoment = moment().format('jYYYY/jMM/jDD'); // Jalali date
Date Picker Configuration
// Bootstrap DatePicker with Persian support
$('.datepicker').datepicker({
language: 'fa',
rtl: true,
format: 'yyyy/mm/dd',
calendarWeeks: true
});
2. Currency Formatting (Iranian Rial)
C# Currency Formatting
// Format as Iranian Rial
var persianCulture = new CultureInfo("fa-IR");
var price = 1000000m;
var formattedPrice = price.ToString("N0", persianCulture); // 1,000,000
// With currency symbol
var priceWithSymbol = $"{formattedPrice} ریال";
// Output: 1,000,000 ریال
// Alternative: Toman (1 Toman = 10 Rial)
var priceInToman = price / 10;
var formattedToman = $"{priceInToman.ToString("N0", persianCulture)} تومان";
// Output: 100,000 تومان
JavaScript Currency Formatting
// Using Intl.NumberFormat
const formatter = new Intl.NumberFormat('fa-IR', {
style: 'decimal',
minimumFractionDigits: 0
});
const price = 1000000;
const formattedPrice = formatter.format(price) + ' ریال';
// Output: ۱,۰۰۰,۰۰۰ ریال
// Helper function
function formatCurrency(amount, currency = 'IRR') {
const formatted = new Intl.NumberFormat('fa-IR').format(amount);
return currency === 'IRR' ? `${formatted} ریال` : `${formatted / 10} تومان`;
}
3. Number Formatting
Persian Digits Conversion
// C# - Convert to Persian digits
public static string ToPersianDigits(this string input)
{
if (string.IsNullOrEmpty(input)) return input;
var persianDigits = new[] { '۰', '۱', '۲', '۳', '۴', '۵', '۶', '۷', '۸', '۹' };
return input.Select(c => char.IsDigit(c) ? persianDigits[c - '0'] : c)
.Aggregate("", (current, next) => current + next);
}
// Usage
var number = "123456";
var persianNumber = number.ToPersianDigits(); // "۱۲۳۴۵۶"
JavaScript Persian Digits
function toPersianDigits(str) {
const persianDigits = ['۰', '۱', '۲', '۳', '۴', '۵', '۶', '۷', '۸', '۹'];
return str.toString().replace(/\d/g, d => persianDigits[d]);
}
// Usage
const number = "123456";
const persianNumber = toPersianDigits(number); // "۱۲۳۴۵۶"
Language Switcher Implementation
Frontend Language Selector
Razor View
@using System.Globalization
@using Microsoft.AspNetCore.Localization
@{
var requestCulture = Context.Features.Get<IRequestCultureFeature>();
var currentCulture = requestCulture?.RequestCulture.Culture ?? CultureInfo.CurrentCulture;
var supportedCultures = new[]
{
new { Code = "en", Name = "English", Flag = "🇬🇧" },
new { Code = "fa", Name = "فارسی", Flag = "🇮🇷" },
new { Code = "ar", Name = "العربية", Flag = "🇸🇦" },
new { Code = "tr", Name = "Türkçe", Flag = "🇹🇷" },
new { Code = "de", Name = "Deutsch", Flag = "🇩🇪" }
};
}
<div class="dropdown">
<button class="btn btn-sm btn-light dropdown-toggle" type="button" data-bs-toggle="dropdown">
@currentCulture.DisplayName
</button>
<ul class="dropdown-menu">
@foreach (var culture in supportedCultures)
{
<li>
<a class="dropdown-item" href="#" onclick="changeLanguage('@culture.Code')">
@culture.Flag @culture.Name
</a>
</li>
}
</ul>
</div>
JavaScript
function changeLanguage(culture) {
// Set culture cookie
document.cookie = `.AspNetCore.Culture=c=${culture}|uic=${culture}; path=/; max-age=31536000`;
// Reload page to apply new culture
window.location.reload();
}
// Or use AJAX to change culture without reload
function changeLanguageAjax(culture) {
abp.utils.setCookieValue(
'.AspNetCore.Culture',
`c=${culture}|uic=${culture}`,
new Date(new Date().getTime() + 365 * 24 * 60 * 60 * 1000),
'/'
);
window.location.reload();
}
Backend Culture Configuration
// In Program.cs or Startup.cs
app.UseRequestLocalization(options =>
{
var supportedCultures = new[]
{
new CultureInfo("en"),
new CultureInfo("fa-IR"),
new CultureInfo("ar"),
new CultureInfo("tr"),
new CultureInfo("de")
};
options.DefaultRequestCulture = new RequestCulture("en");
options.SupportedCultures = supportedCultures;
options.SupportedUICultures = supportedCultures;
// Cookie-based culture provider
options.RequestCultureProviders.Insert(0, new CookieRequestCultureProvider());
});
Translation Management
Adding New Translations
Step 1: Define Keys in Base Language (en.json)
{
"culture": "en",
"texts": {
"Product:Name": "Product Name",
"Product:Price": "Price",
"Product:Stock": "Stock Quantity",
"Product:AddedSuccessfully": "Product added successfully",
"Product:UpdatedSuccessfully": "Product updated successfully"
}
}
Step 2: Add Translations to Other Languages
// fa.json (Persian)
{
"culture": "fa",
"texts": {
"Product:Name": "نام محصول",
"Product:Price": "قیمت",
"Product:Stock": "موجودی",
"Product:AddedSuccessfully": "محصول با موفقیت اضافه شد",
"Product:UpdatedSuccessfully": "محصول با موفقیت بهروزرسانی شد"
}
}
// ar.json (Arabic)
{
"culture": "ar",
"texts": {
"Product:Name": "اسم المنتج",
"Product:Price": "السعر",
"Product:Stock": "الكمية المتاحة",
"Product:AddedSuccessfully": "تمت إضافة المنتج بنجاح",
"Product:UpdatedSuccessfully": "تم تحديث المنتج بنجاح"
}
}
Step 3: Use in Code
// C#
var message = L["Product:AddedSuccessfully"];
// Razor
<label>@L["Product:Name"]</label>
// JavaScript
const message = abp.localization.localize('Product:AddedSuccessfully', 'Shop');
Localization Key Naming Conventions
| Pattern | Example | Usage |
|---|---|---|
Menu:* |
Menu:Home, Menu:Products |
Navigation menu items |
Button:* |
Button:Save, Button:Cancel |
Button labels |
Message:* |
Message:Success, Message:Error |
User messages and notifications |
Entity:Property |
Product:Name, Order:Date |
Entity property labels |
Validation:* |
Validation:Required, Validation:MaxLength |
Validation error messages |
Action:* |
Action:Create, Action:Update |
Action descriptions |
Best Practices
DO's
- Use consistent key naming - Follow the pattern convention (Menu:*, Button:*, etc.)
- Always define keys in base language first - Use English (en.json) as the base
- Keep translations in sync - When adding new keys, update all language files
- Use descriptive key names -
Product:AddedSuccessfullynotMsg1 - Test RTL layout thoroughly - Ensure proper alignment for Persian/Arabic
- Use culture-specific formatting - Dates, numbers, currency based on culture
- Provide fallback translations - Missing translations should fallback to English
- Use parameters for dynamic content -
L["Welcome:User", userName]
DON'Ts
- Don't hardcode text in views - Always use localization keys
- Don't mix RTL and LTR in same element - Use proper Unicode direction markers
- Don't forget to test all languages - Ensure UI doesn't break with long translations
- Don't duplicate keys - Reuse common translations across the application
- Don't translate technical terms unnecessarily - Keep product/brand names in original language
- Don't forget about JavaScript strings - Client-side messages need localization too
Translation Contribution Guidelines
How to Contribute Translations
Step 1: Fork the Repository
git clone https://github.com/shirinzad/shop.git
cd shop
Step 2: Add or Update Language File
Navigate to: src/Shirinzad.Shop.Domain.Shared/Localization/Shop/
- To add a new language: Create a new file (e.g.,
fa.jsonfor Persian) - To update existing: Modify the appropriate JSON file
Step 3: Validate JSON Format
{
"culture": "fa",
"texts": {
"Key1": "Translation 1",
"Key2": "Translation 2"
}
}
Step 4: Submit Pull Request
git checkout -b add-persian-translations
git add src/Shirinzad.Shop.Domain.Shared/Localization/Shop/fa.json
git commit -m "Add Persian (fa) translations"
git push origin add-persian-translations
Translation Quality: Ensure translations are contextually accurate, culturally appropriate, and follow the tone of the application.
Additional Resources
Libraries and Tools
- Bootstrap RTL - RTL version of Bootstrap CSS framework
- Luxon - Modern date/time library with i18n support
- Moment.js with Jalali - Persian calendar support
- Bootstrap DatePicker - Date picker with multi-language support
- jQuery Timeago - Relative time formatting with Persian support
- SweetAlert2 - Beautiful alerts with RTL support
- Select2 - Advanced select box with RTL support