پشتیبانی از چند زبانه ای و محلی سازی Localization در اندروید
برنامه ها شامل منابعی هستند که میتواند مختص به یک فرهنگ خاص باشد. برای مثال، برنامه میتواند شامل رشته های فرهنگ خاص باشد که به زبان محلی فعلی ترجمه شده است. این یک تمرین خوب برای نگه داشتن منابع فرهنگ خاص است که از بقیه برنامه شما جدا شده است. اندروید منابع زبانها و فرهنگهای خاص را بر اساس تنظیمات محلی سیتم حل میکند. شما میتوانید با استفاده از منابع فرهنگ لغت در پروژه اندرویدی خود، از محلهای مختلف پشتیبانی کنید.
شما می توانید منابع درخور با فرهنگ افرادی که از برنامه شما استفاده می کنند، را مشخص کنید. شما می توانید هر نوع منبع مناسب برای زبان و فرهنگ کاربران خود را تهیه کنید. برای مثال، تصویر زیر تصویری از یک برنامه را نشان می دهد که رشته ها و منابع تصویری (drawable) در آن دستگاهها به طور پیش فرض زبان محلی (en_US) و زبان اسپانیایی (es_ES) نمایش داده می شود.
شکل 1. برنامه از منابع مختلف وابسته به محل فعلی استفاده میکند.
اگر پروژه خود را با استفاده از Android SDK Tools ایجاد کردید، ابزارها در سطح بالای پروژه یک شاخه /res ایجاد میکنند. در داخل این شاخه res/ زیرشاخه هایی برای انواع منابع مختلف وجود دارد. همچنین چند فایل پیشفرض مانند res/values/strings.xml هست که مقدارهای رشتهای را نگه میدارد.
پشتیبانی از زبان های مختلف فراتر از استفاده از منابع خاص محلی میرود. بعضی کاربران یک زبان را انتخاب میکنند که از اسکریپت های راست به چپ (RTL) مانند عربی , فارسی یا عبری برای زبان محلی خود استفاده می کنند. سایر کاربران در یک زبان که از اسکریپت RTL استفاده می کند، میبینند یا محتوا تولید میکنند ، حتی اگر یک زبان را با استفاده از اسکریپت های LTR مانند زبان انگلیسی، به عنوان UI محلی خود تنظیم کرده باشند. برای پشتیبانی از هر دو نوع کاربران، برنامه شما نیاز دارند موارد زیر را انجام دهد:
- یک طرح UI RTL را برای محلهای RTL بکار ببرید.
- شناسایی و اعلام مسیر اطلاعات متنی که در پیام های فرمت شده نمایش داده می شود. معمولاً، شما فقط می توانید یک روش را انتخاب کنید که متن اطلاعات را برای شما تعیین می کند.
ایجاد راهنماهای محلی و فایلهای منبع
برای افزودن پشتیبانی از مکانهای بیشتر، یک فهرست در داخل res/ ایجاد کنید. نام هر فهرست باید مانند فرمت زیر بچسبد:
<resource type>-b+<language code>[+<country code>]
برای مثال، مقدار -b+es/ شامل منابع رشته ای برای محل هایی با کد زبان es است. به همین ترتیب، mipmap-b+es+ES/ شامل آیکن برای محلهایی با کد زبان es و کد کشور ES است. اندروید منابع مناسب را با توجه به تنظیمات محلی دستگاه در زمان اجرا بارگذاری میکند.
پس از تصمیم گیری درباره محلهای پشتیبانی، منابع زیر شاخه ها و فایلها را ایجاد کنید.
res/
values
strings.xml
values-b+es/
strings.xml
mipmap/
country_flag.png
mipmap-b+es+ES/
country_flag.png
برای مثال، بعضی از فایلهای منابع مختلف برای زبانهای مختلف هستند:
رشته های انگلیسی (محل پیش فرض) /values/strings.xml :
<resources>
<string name="hello_world">Hello World!</string>
</resources>
رشته های اسپانیایی (محلی es) /values-es/strings.xml :
<
resources
>
<
string
name
=
"hello_world"
>¡Hola Mundo!</
string
>
</
resources
>
نماد پرچم ایالات متحده امریکا (محل پیش فرض) /mipmap/country_flag.png:
شکل 2- آیکن استفاده شده برای محل پیش فرض (en_US) ایالات متحده امریکا
پرچم کشور اسپانیا (محل es_ES)، /mipmap-b+es+ES/country_flag.png :
پرچم کشور اسپانیا
// Get a string resource from your app's Resources
String hello = getResources().getString(R.string.hello_world);
// Or supply a string resource to a method that requires a string
TextView textView = new TextView(this);
textView.setText(R.string.hello_world);
در دیگر فایلهای xml، شما می¬توانید هر زمان که xml یک مقدار سازگار را قبول می¬کند، به منبع با @/ مراجعه کنید.
برای مثال:
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/country_flag" />
فرمت متن در پیامها
یکی از شایع ترین وظایف مشترک در برنامه، قالب بندی متن است. پیام های محلی شده با قرار دادن متن و داده های عددی در موقعیت های مناسب فرمت می شوند. متاسفانه، هنگامی که با داده های UI RTL یا RTL برخورد می شود، قالب بندی ساده می تواند خروجی متن نادرست یا حتی خواندن متن را نمایش دهد.
زبان هایی مانند عربی، عبری، فارسی و اردو به طور کلی در جهت راست به چپ (RTL) نوشته شده است. با این حال برخی از عناصر آنها مانند اعداد و متن چپ به راست (LTR) در جهت LTR در متن متناظر RTL جاسازی و نوشته شده¬اند. زبان هایی که از اسکریپت LTR استفاده می کنند، از جمله انگلیسی نیز دو طرفه هستند، زیرا آنها می توانند شامل اسکریپت های RTL جاسازی شده باشند که باید در جهت RTL نمایش داده شوند.
اغلب اوقات، برنامه ها خودشان این چنین نمونه هایی از جاسازی متن جهت مخالف ایجاد می کنند. آنها داده های متنی یک زبان دلخواه و یک جهت متن دلخواه را به پیام های محلی اضافه می کنند. این مخلوط کردن جهات اغلب شامل نشانه مشخصی از جایی که متن جهت مخالف شروع می شود و به پایان می رسد نیست. این ویژگی های متن تولید شده برنامه باعث بیشتر مشکلات می شود.
اگر چه سیستم پیش فرض بررسی، معمولاً متون دو طرفه متن را ارائه می¬دهد. انتظار می¬رود که ممکن است وقتی برنامه آن را داخل پیام محلی قرار می¬دهد، به درستی ارائه ندهد. موقعیت های زیر مثال هایی از مواردی را ارائه می دهند که احتمالا متن به درستی ظاهر نمی شود:
در همان ابتدای پیام درج شده است:
PERSON_NAME شما را صدا می¬زند.
با عدد شروع می شود، مانند آدرسها یا شماره تلفن:
3210- 654 987
با نقطه گذاری شروع می شود، مانند شماره تلفن:
19876543210+
با نقطه گذاری تمام می شود:
شما مطمئن هستید؟
شامل هر دو جهت است:
کلمه בננה عبری برای موز است.
مثال
به عنوان مثال، فرض کنید که برنامه گاهی اوقات نیاز به نمایش پیام “منظورت این بود S%” ، با آدرس وارد شده به جایS% در زمان اجرا دارد. با اینکه برنامه از محل¬های مختلف UI پشتیبانی می کند، پیام از یک منبع خاص محلی می¬رسد و در جهت RTL استفاده می¬شود وقتی که از یک زبان محلی استفاده کنید. برای UI عبری، باید به صورت زیر ظاهر شود:
האם התכוונת ל %s?
این پیشنهاد، با این حال، ممکن است از یک پایگاه داده باشد که شامل متن در زبان محلی نمی شود. برای مثال، اگر آدرس مورد نظر در سوال برای یک مکان در کالیفرنیا باشد، در پایگاه داده با استفاده از متن انگلیسی ظاهر می شود. اگر آدرس “15 Bay Street, Laurel, CA ” را در پیام RTL بدون ارائه نکات در مورد جهت متن وارد کنید، نتیجه مورد انتظار صحیح نیست:
האם התכוונת ל 15 Bay Street, Laurel, CA?
توجه داشته باشید که شماره خانه در سمت راست آدرس ظاهر می شود، نه در سمت چپ عنوان در نظر گرفته شده، و باعث می شود که شماره خانه بیشتر شبیه یک کد پستی عجیب و غریب باشد. همین مسئله ممکن است اگر متن RTL را در یک پیام که جهت متن LTR استفاده می کند، وارد کنید، رخ دهد.
توضیح و راه حل
مشکل در مثال قبلی به این دلیل اتفاق می افتاد که قالب بندی متن مشخص نمی کند که “15” بخشی از آدرس است، بنابراین سیستم نمی تواند تعیین کند که آیا “15” بخشی از متن RTL است که قبل از متن LTR ارائه می شود یا بعد از آن.
برای حل این مشکل، از روش unicodeWrap() که در کلاس BidiFormatter یافت می شود، در هر قطعه ای از متن که شما در یک پیام محلی قرار داده اید، استفاده می¬شود. تنها زمانی که شما نباید از unicodeWrap() استفاده کنید عبارتند از:
متن در یک رشته ماشین قابل خواندن قرار می گیرد، مانند یک URI یا پرس و جو SQL.
شما می دانید که قطعه متن به درستی بسته شده است.
روش unicodeWrap() جهت یک رشته را تشخیص می دهد و آن را در خط های قالب بندی یونیکد که این جهت را اعلام می کند، پنهان می کند. برای اینکه حالا “15” در داخل متن ظاهر می شود به عنوان LTR اعلام شده است و در موقعیت صحیح نمایش داده می شود:
האם התכוונת ל 15 Bay Street, Laurel, CA?
قطعه کد زیر نشان می دهد که چگونه از unicodeWrap() استفاده کنید:
String mySuggestion = "15 Bay Street, Laurel, CA";
BidiFormatter myBidiFormatter = BidiFormatter.getInstance();
// The "did_you_mean" localized string resource includes
// a "%s" placeholder for the suggestion.
String.format(R.string.did_you_mean,
myBidiFormatter.unicodeWrap(mySuggestion));
توجه: اگر برنامه شما برای (Android 4.3 (API level 18 یا بالاتر است، از نسخه BidiFormatter موجود در چارچوب Android استفاده کنید. در غیر این صورت، از نسخه BidiFormatter موجود در کتابخانه پشتیبانی استفاده کنید.
فرمت اعداد
استفاده از فرمت رشته¬ها، نه متدهای تماس، برای تبدیل اعداد به رشته در برنامه بصورت زیر عمل می¬کنیم:
String myIntAsString = String.format("%d", myInt);
این دستور اعداد را به طور مناسب برای زبان محلی شما فرمت می کند، که ممکن است شامل استفاده از مجموعه ای متفاوت از رقم ها باشد.
هنگام استفاده از String.format() برای ایجاد یک پرس و جوی SQL در یک دستگاه که از مجموعه ای از ارقام محلی خود استفاده می کند، مانند زبان فارسی و بیشتر مناطق عربی، اگر هر یک از پارامترهای پرس و جو اعداد باشد، مشکلاتی رخ می دهد. این به این دلیل است که عدد در فرمتهای محلی فرمت شده است و این رقمها در SQL معتبر نیستند.
برای حفظ فرمت اعداد ASCII و حفظ پرس و جو معتبر، شما باید به جای استفاده بیش از حد از نسخه String.format() که شامل محلی به عنوان پارامتر اول است. استدلال محلی باید Locale.US باشد.
پشتیبانی از معکوس کردن طرح¬ (Layout)
افرادی که از اسکریپت RTL (راست به چپ) استفاده می کنند، یک رابط کاربری RTL را ترجیح می دهند که شامل منوهای راست چین شده، متن راست چین و پیکان هایی رو به جلو به سمت چپ است.
شکل 4 مقایسه بین نسخه LTR صفحه نمایش در تنظیمات برنامه و رونوشت RTL آن را نشان می دهد:
شکل 4- انواع LTR و RTL از یک صفحه نمایش
هنگام اضافه کردن پشتیبانی RTL به برنامه خود، مهم است که نکات زیر را در ذهن داشته باشید:
- معکوس کردن متن RTL در دستگاه های دارای سیستم عامل Android 4.2 (سطح API 17) یا بالاتر پشتیبانی می شود.
- برای تست اینکه آیا برنامه شما از جهت متنی RTL پشتیبانی می کند، با استفاده از گزینه های توسعه (Developer Option) آزمایش کنید و از افرادی که از اسکریپت RTL استفاده می کنند در برنامه خود دعوت کنید.
نکته: برای مشاهده طراحی دستورالعمل های اضافی مربوط به معکوس کردن لایوت، از جمله لیستی که باید و نباید معکوس کردن باشد، دستورالعمل طراحی متریال Bidirectionality را ببینید.
برای معکوس کردن UI در برنامه خود، به طوری که RTL در یک زبان RTL ظاهر شود، مراحل زیر را انجام دهید.
تغییر ساختار و فایل های مانیفست
فایل build.gridle ماژول برنامه و فایل manifest برنامه خود را به صورت زیر تغییر دهید:
build.gradle (Module: app)
AndroidManifest.xml
نکته: اگر برنامه شما به اندروید 4.1.1 (سطح API 16) یا پایین تر برسد، ویژگی android:supportsRtl، نادیده گرفته می شود، همراه با مقادیر ویژگی شروع و پایان که در فایل های لایوت برنامه شما ظاهر می شود. در این حالت، معکوس کردن طرح بندی RTL به طور خودکار در برنامه رخ نمی دهد.
به روزرسانی منابع موجود
به ترتیب در سمت چپ و راست برای شروع و پایان، در هر یک از فایل های منبع موجود خود را تغییر دهید. با انجام این کار، به چارچوب اجازه می دهید که عناصر UI برنامه خود را براساس تنظیمات زبان کاربر تنظیم کند.
توجه: قبل از بهروزرسانی منابع خود، نحوه ارائه پشتیبانی از برنامههای قدیمی و یا برنامههایی که با اندروید 4.1.1 (سطح API 16) و پایینتر قرار دارند، یاد بگیرید.
برای استفاده از قابلیت هماهنگ سازی چارچوب RTL، ویژگیها را در فایل های لایوت خود که در جدول 1 نمایش داده شده، تغییر دهید.
جدول 1. ویژگی هایی که با استفاده از آنها برنامه شما از چندین جهت متن پشتیبانی میکند
جدول 2 نشان می دهد که چگونه سیستم با استفاده از ویژگی های تراز بندی UI بر اساس target SDK version ، چه ویژگی های چپ و راست تعریف می شود و چه ویژگی های شروع و پایان تعریف می شود.
جدول 2. رفتار عنصر UI بر اساس target SDK version و ویژگی های تعریف شده
Targeting Android 4.2 (API level 17) or higher? | Left and right defined? | Start and end defined? | Result |
Yes | Yes | Yes | start and end resolved, and override left and right |
Yes | Yes | No | Only left and right are used |
Yes | No | Yes | Only start and end are used |
No | Yes | Yes | left and right are used (start and end are ignored) |
No | Yes | No | Only left and right are used |
No | No | Yes | start and end resolved to left and right |
اضافه کردن جهت و منابع خاص زبان
این مرحله شامل اضافه کردن نسخه های خاص لایوت، drawables و ارزش های فایل های منبع است که دارای مقادیر سفارشی برای زبان های مختلف و جهت متن هستند.
در اندروید 4.2 (API level 17) و بالاتر، می توانید از مقادیر منابع -ldrtl (جهت لایوت راست به چپ) و –ldltr (جهت لایوت چپ به راست) استفاده کنید. برای حفظ سازگاری مجدد با بارگیری منابع موجود، نسخه های قدیمی تر از اندروید از زبان شناسان زبان منبع، برای هدایت جهت صحیح متن استفاده می کنند.
فرض کنید شما می خواهید یک فایل لایوت خاص برای پشتیبانی از اسکریپت های RTL، مانند زبان عبری، عربی و فارسی اضافه کنید. برای انجام این کار، شما یک فهرست layout-ldrtl/ در فهرست res/ اضافه می کنید، همانطور که در مثال زیر نشان داده شده است:
res/
layout/
main.xml This layout file is loaded by default.
layout-ldrtl/
main.xml This layout file is loaded for languages using an
RTL text direction, including Arabic, Persian, and Hebrew
اگر می خواهید یک نسخه خاص از لایوت که فقط برای متن عربی طراحی شده است را اضافه کنید، ساختار فهرست شما به به صورت زیر است:
res/
layout/
main.xml This layout file is loaded by default.
layout-ar/
main.xml This layout file is loaded for Arabic text.
layout-ldrtl/
main.xml This layout file is loaded only for non-Arabic
languages that use an RTL text direction.
نکته: منابع اختصاصی زبان، منابع اختصاصی خاصی هستند که اهمیت بیشتری نسبت به منابع پیش فرض دارند.
استفاده از ویجتهای پشتیبانی شده
همانطور که از آندروید 4.2 (API سطح 17)، اکثر عناصر چارچوب رابط کاربر از متن RTL به طور خودکار پشتیبانی می کند. با این حال، چندین عنصر چارچوب، مانند ViewPager، جهت متن RTL را پشتیبانی نمی کنند.
ویجت های روی صفحه نمایش از متن RTL پشتیبانی می کنند تا زمانی که فایلهای manifest مربوطه شامل ویژگی زیر باشد.
android:supportsRtl=”true”
آماده کردن پشتیبانی از برنامه های قدیمی
اگر برنامه شما targets Android 4.1.1 (سطح API 16) یا پایین تر باشد، همچنین علاوه بر ویژگیهای چپ و راست، شروع و پایان را نیز شامل می شود.
برای بررسی اینکه آیا لایوت شما باید از جهت متن RTL استفاده کند، از دستورات زیر استفاده کنید:
private boolean shouldUseLayoutRtl() {
if (android.os.Build.VERSION.SDK_INT >=
android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) {
return View.LAYOUT_DIRECTION_RTL == getLayoutDirection();
} else {
return false;
}
}
توجه: برای جلوگیری از مسائل سازگاری، از نسخه 23.0.1 یا بالاتر از ابزارهای Android SDK Build Tools استفاده کنید.
تست با استفاده از گزینه های توسعه دهنده
در دستگاههایی که از Android 4.4 (API level 19) یا بالاتر استفاده می کنند، می توانید جهت لایوت RTL را در گزینه های توسعه دهنده دستگاه خود فعال کنید. این تنظیم به شما اجازه می دهد که متن را با استفاده از اسکریپت LTR، مانند متن انگلیسی، در حالت RTL مشاهده کنید.
به روز رسانی منطق برنامه
این بخش مکان های خاصی را در منطق برنامه شما توضیح می دهد که هنگام انطباق برنامه خود برای دست زدن به چند جهت متن، باید به روزرسانی کنید.
تغییرات ویژگیها
برای رسیدگی به تغییر در هر ویژگی مرتبط با RTL، مانند جهت لایوت، پارامترهای لایوت، پیمایش، جهت متن، ترازبندی متن، و یا موقعیت drawable- شما می توانید از onRtlPropertiesChanged() استفاده کنید. این فراخوانی به شما امکان می دهد جهت فعلی لایوت را به دست آورید و بر اساس یک اکتیویتی اشیا را به روز کنید.
View ها ( اشیاء)
اگر شما یک ویجت UI ایجاد کنید که به طور مستقیم بخشی از یک سلسله مراتب View اکتیویتی نیست، مانند یک محاوره یا یک عنصر UI مانند toast، بسته به نوع context را انتخاب کنید. قطعه کد زیر نشان می دهد چگونه برای تکمیل این فرآیند عمل کنید:
final Configuration config =
getContext().getResources().getConfiguration();
view.setLayoutDirection(config.getLayoutDirection());
چند روش از کلاس View نیاز به توجه بیشتر دارد:
onMeasure()
اندازه گیری ها ممکن است بسته به جهت متن متفاوت باشد.
onLayout()
اگر شما پیاده سازی لایوت خود را ایجاد کنید، باید در نسخه ی شما onLayout() از super() صدا زده شود و منطق سفارشی خود را برای پشتیبانی از اسکریپت های RTL تطبیق دهید.
onDraw()
اگر شما یک نمایه سفارشی یا اضافه کردن قابلیت های پیشرفته به نقاشی را اجرا می کنید، باید کد خود را برای پشتیبانی از اسکریپت های RTL به روز کنید. از کد زیر استفاده کنید تا تعیین کنید که ویجت شما در حالت RTL است:
// On devices running Android 4.1.1 (API level 16) and lower,
// you can call the isLayoutRtl() system method directly.
public boolean isLayoutRtl() {
return (getLayoutDirection() == LAYOUT_DIRECTION_RTL);
}
Drawable ها
اگر یک drawable داشته باشید که برای یک لایوت RTL معکوس شده باشد، یکی از این مراحل را بر اساس نسخه Android که در دستگاه اجرا می شود، کامل کنید:
- در دستگاه هایی که از Android 4.3 (API level 18) و پایینتر استفاده می کنند، شما باید فایلهای منبع -ldrtl را اضافه و تعریف کنید.
- در Android 4.4 (API level 19) و بالاتر، می توانید از android:autoMirrored=”true” استفاده کنید، هنگام تعریف مجدد Drawable، اجازه می دهد تا سیستم را برای معکوس سازی طرح RTL آماده کنید.
توجه: ویژگی android:autoMirrored فقط برای drawable های ساده کار می کند که دو برابر شدن معکوس دو طرفه به سادگی یک معکوس گرافیکی از کل drawable است. اگر drawable شما شامل عناصر متعدد است، و یا اگر منعکس کننده drawable شما می تواند ترجمه خود را تغییر دهید، شما باید خود معکوس را انجام دهد. هر گاه امکان پذیر است، با یک کارشناس دو طرفه بررسی کنید تا مشخص شود آیا drawables های معکوس شما برای کاربران حساس هستند یا خیر.
درجه کشش (Gravity)
اگر کد برنامه شما از Gravity.LEFT یا Gravity.RIGHT استفاده کند، باید این مقادیر را به Gravity.START و Gravity.END تغییر دهید.
به عنوان مثال، اگر شما از کد زیر استفاده می کنید:
switch (gravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
case Gravity.LEFT:
// Handle objects that are left-aligned.
break;
case Gravity.RIGHT:
// Handle objects that are right-aligned.
break;
}
باید آن را به صورت زیر تغییر دهید:
final int layoutDirection = getLayoutDirection();
final int absoluteGravity =
Gravity.getAbsoluteGravity(gravity, layoutDirection);
switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
case Gravity.LEFT:
// Handle objects that are left-aligned.
break;
case Gravity.RIGHT:
// Handle objects that are right-aligned.
break;
}
این بدان معنی است که شما می توانید کد فعلی خود را نگه دارید که مقادیر چپ چین و راست چین را کنترل می کند حتی اگر از شروع و پایان دادن به مقادیر Gravity خود استفاده کنید.
نکته: هنگام استفاده از تنظیمات Gravity، از یک نسخه بیش از حد Gravity.apply() استفاده کنید که شامل یک آرگومان layoutDirection است.
حاشیه بیرونی و حاشیه خارجی (Margin and Padding)
برای پشتیبانی از اسکریپت های RTL در برنامه خود، به دنبال بهترین شیوه های مربوط به ارزش حاشیه و فاصله داخلی باشید:
- از getMarginStart() و getMarginEnd()به جای معادلهای مشخصه جهت، leftMargin و RightMargin استفاده کنید.
- هنگام استفاده از setMargins() اگر برنامه شما اسکریپت RTL را تشخیص دهد، ارزش های استدلال چپ و راست را مبادله می کند.
اگر برنامه شما شامل منطق Padding سفارشی باشد، ()setPadding و ()setPaddingRelative را نادیده بگیرید.
دیدگاهتان را بنویسید