منابع رشته‌ای

منبع رشته‌ای، رشته‌های نوشتاری را با قالب‌بندی و سبک نوشتاری اختیاری برای برنامه‌تان ارائه می‌دهد. سه نوع منبع وجود دارد که می‌تواند رشته‌ها را برای برنامه شما فراهم کند:

رشته
منبع XML که یک رشته ارائه می‌دهد.
آرایه رشته‌ای
منبع XML که آرایه‌ای از رشته‌ها را ارائه می‌دهد.
رشته‌های کمیت (جمع)
منبع XML که رشته‌های مختلفی برای جمع‌بندی دارد.

همه رشته‌ها می‌توانند برخی نشانه‌گذاری‌های سبک‌بندی و آرگومان‌های قالب‌بندی را اعمال کنند. برای اطلاعات درباره رشته‌های قالب‌بندی و سبک‌بندی، به بخش مربوط به قالب‌بندی و سبک‌بندی مراجعه کنید.

رشته

رشته‌ای تکی که می‌توان از کد برنامه (مانند تابع ترکیب‌پذیر) یا از فایل‌های منبع دیگر به آن ارجاع داد.

مکان فایل:
res/values/filename.xml
نام فایل اختیاری است. name عنصر <string> به‌عنوان شناسه منبع استفاده می‌شود.
نوع داده منبع کامپایل‌شده:
اشاره‌گر منبع به String.
مرجع منبع:
در Kotlin: R.string.string_name
در XML: @string/string_name
نحو:
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string
        name="string_name"
        >text_string</string>
</resources>
عناصر:
<resources>
الزامی. این باید گره ریشه باشد.

بدون مشخصه.

<string>
رشته‌ای که می‌تواند شامل برچسب‌های سبک‌بندی باشد. توجه داشته باشید که باید از آپاستروف و علامت نقل‌قول استفاده کنید. برای اطلاعات بیشتر درباره نحوه قالب‌بندی و سبک‌دهی مناسب رشته‌ها، به بخش قالب‌بندی و سبک‌دهی در زیر مراجعه کنید.

مشخصه‌ها:

name
رشته. نامی برای رشته. این نام به‌عنوان شناسه منبع استفاده می‌شود.
مثال:
فایل XML در res/values/strings.xml ذخیره شد:
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="hello">Hello!</string>
</resources>

این کد برنامه رشته‌ای را از داخل یک composable با stringResource() بازیابی می‌کند:

@Composable
fun Greeting() {
    Text(text = stringResource(R.string.hello))
}

توجه: برای بازیابی رشته‌ای خارج از تابع ترکیب‌پذیر، از context.getString(R.string.hello) استفاده کنید.

همچنین می‌توانید به منابع رشته از فایل‌های XML دیگر، مثل AndroidManifest.xml، ارجاع دهید:
<activity
    android:name=".MainActivity"
    android:label="@string/hello" />

آرایه رشته‌ای

آرایه‌ای از رشته‌ها که می‌توان از برنامه به آن‌ها ارجاع داد.

مکان فایل:
res/values/filename.xml
نام فایل اختیاری است. name عنصر <string-array> به‌عنوان شناسه منبع استفاده می‌شود.
نوع داده منبع کامپایل‌شده:
اشاره‌گر منبع به آرایه‌ای از String.
مرجع منبع:
در Kotlin: R.array.string_array_name
در XML: @[package:]array/string_array_name
نحو:
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string-array
        name="string_array_name">
        <item
            >text_string</item>
    </string-array>
</resources>
عناصر:
<resources>
الزامی. این باید گره ریشه باشد.

بدون مشخصه.

<string-array>
آرایه‌ای از رشته‌ها را تعریف می‌کند. شامل یک یا چند عنصر <item> است.

مشخصه‌ها:

name
رشته. نامی برای آرایه. این نام به‌عنوان شناسه منبع برای ارجاع به آرایه استفاده می‌شود.
<item>
رشته‌ای که می‌تواند شامل برچسب‌های سبک‌بندی باشد. این مقدار می‌تواند ارجاعی به منبع رشته‌ای دیگری باشد. باید فرزند عنصر <string-array> باشد. توجه داشته باشید که باید از آپاستروف و علامت نقل‌قول استفاده کنید. برای اطلاعات درباره نحوه قالب‌بندی و سبک‌دهی مناسب رشته‌ها، به قالب‌بندی و سبک‌دهی در زیر مراجعه کنید.

بدون مشخصه.

مثال:
فایل XML در res/values/strings.xml ذخیره شد:
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string-array name="planets_array">
        <item>Mercury</item>
        <item>Venus</item>
        <item>Earth</item>
        <item>Mars</item>
    </string-array>
</resources>

این کد برنامه آرایه رشته‌ای را از داخل یک ترکیب‌پذیر با stringArrayResource() بازیابی می‌کند:

@Composable
fun PlanetList() {
    val planets: Array =
        stringArrayResource(R.array.planets_array)
    // Render the array, e.g. inside a LazyColumn.
}

توجه: برای بازیابی آرایه رشته‌ای خارج از تابع ترکیب‌پذیر، از context.resources.getStringArray(R.array.planets_array) استفاده کنید.

رشته‌های کمیت (جمع)

زبان‌های مختلف قوانین متفاوتی برای مطابقت دستوری با کمیت دارند. برای مثال، در زبان انگلیسی، مقدار ۱ یک مورد خاص است. می‌نویسیم «۱ کتاب»، اما برای هر تعداد دیگری می‌نویسیم «n کتاب». این تمایز بین مفرد و جمع بسیار رایج است، اما زبان‌های دیگر تمایزهای دقیق‌تری قائل می‌شوند. مجموعه کاملی که Android پشتیبانی می‌کند عبارت است از zero، one، two، few، many، و other.

قوانین تصمیم‌گیری درباره اینکه از کدام حالت برای زبان و کمیت مشخصی استفاده شود می‌تواند بسیار پیچیده باشد، بنابراین Android روش‌هایی مانند pluralStringResource() را برای انتخاب منبع مناسب برای شما ارائه می‌دهد.

اگرچه در گذشته به آن‌ها «رشته‌های کمیت» گفته می‌شد (و هنوز هم در میانای برنامه‌سازی کاربردی به این نام خوانده می‌شوند)، رشته‌های کمیت باید فقط برای جمع‌ها استفاده شوند. استفاده از رشته‌های کمیت برای پیاده‌سازی چیزی مانند «صندوق ورودی» Gmail درمقابل «صندوق ورودی (۱۲)» (مثلاً وقتی پیام‌های خوانده‌نشده وجود دارد) اشتباه است. ممکن است استفاده از رشته‌های کمیت به‌جای عبارت if راحت‌تر به‌نظر برسد، اما توجه به این نکته مهم است که برخی زبان‌ها (مانند چینی) اصلاً این تمایزات دستوری را قائل نمی‌شوند، بنابراین همیشه رشته other را دریافت خواهید کرد.

انتخاب رشته مورداستفاده صرفاً براساس ضرورت دستوری انجام می‌شود. در زبان انگلیسی، رشته مربوط به zero نادیده گرفته می‌شود حتی اگر مقدار آن ۰ باشد، زیرا ۰ ازنظر دستوری با ۲ یا هر عدد دیگری به‌جز ۱ تفاوتی ندارد («zero books»،‏ «one book»،‏ «two books»، و غیره). برعکس، در زبان کره‌ای فقط رشته other استفاده می‌شود.

Don't be misled either by the fact that, say, two sounds like it could only apply to the quantity 2: a language may require that 2, 12, 102 (and so on) are all treated like one another but differently to other quantities. به مترجمتان اعتماد کنید تا بدانید زبان آن‌ها واقعاً بر چه تفاوت‌هایی تأکید دارد.

اگر پیام شما حاوی عدد کمیت نیست، احتمالاً گزینه خوبی برای جمع نیست. برای مثال، در زبان لیتوانیایی، از شکل مفرد برای هر دو عدد ۱ و ۱۰۱ استفاده می‌شود، بنابراین «۱ کتاب» به‌صورت «1 knyga» و «۱۰۱ کتاب» به‌صورت «101 knyga» ترجمه می‌شود. درعین‌حال، «یک کتاب» می‌شود «knyga» و «کتاب‌های زیاد» می‌شود «daug knygų». اگر پیام جمع انگلیسی حاوی «a book» (مفرد) و «many books» (جمع) بدون عدد واقعی باشد، می‌توان آن را به‌صورت «knyga» (یک کتاب)/«daug knygų» (کتاب‌های زیاد) ترجمه کرد، اما با قوانین لیتوانیایی، وقتی عدد ۱۰۱ باشد، «knyga» (یک کتاب) را نشان می‌دهد.

اغلب می‌توان بااستفاده از فرمول‌بندی‌های خنثی ازنظر کمیت، مانند «کتاب‌ها: ۱»، از رشته‌های کمیت اجتناب کرد. اگر این سبک برای برنامه‌تان قابل‌قبول باشد، کار شما و مترجمانتان را آسان‌تر می‌کند.

در API 24+ می‌توانید به‌جای آن از کلاس ICU MessageFormat که بسیار قدرتمندتر است استفاده کنید.

مکان فایل:
res/values/filename.xml
نام فایل اختیاری است. name عنصر <plurals> به‌عنوان شناسه منبع استفاده می‌شود.
مرجع منبع:
در Kotlin: R.plurals.plural_name
نحو:
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <plurals
        name="plural_name">
        <item
            quantity=["zero" | "one" | "two" | "few" | "many" | "other"]
            >text_string</item>
    </plurals>
</resources>
عناصر:
<resources>
الزامی. این باید گره ریشه باشد.

بدون مشخصه.

<plurals>
مجموعه‌ای از رشته‌ها که یکی از آن‌ها بسته به مقدار چیزی ارائه می‌شود. شامل یک یا چند عنصر <item> است.

مشخصه‌ها:

name
رشته. نامی برای جفت رشته‌ها. این نام به‌عنوان شناسه منبع استفاده می‌شود.
<item>
رشته جمع یا مفرد. این مقدار می‌تواند ارجاعی به منبع رشته‌ای دیگری باشد. باید فرزند عنصر <plurals> باشد. توجه داشته باشید که باید از آپاستروف و علامت نقل‌قول استفاده کنید. برای اطلاعات درباره نحوه قالب‌بندی و سبک‌دهی مناسب رشته‌ها، به بخش قالب‌بندی و سبک‌دهی در زیر مراجعه کنید.

مشخصه‌ها:

quantity
کلیدواژه. مقداری که نشان می‌دهد چه زمانی باید از این رشته استفاده شود. مقادیر معتبر، با مثال‌های غیرجامع در پرانتز:
مقدارشرح
zeroوقتی زبان نیاز به رفتار خاصی با عدد ۰ دارد (مانند عربی).
oneوقتی زبان به رفتار خاصی با اعداد مانند یک نیاز دارد (مانند عدد ۱ در انگلیسی و اکثر زبان‌های دیگر؛ در روسی، هر عددی که به ۱ ختم می‌شود اما به ۱۱ ختم نمی‌شود در این کلاس قرار دارد).
twoوقتی زبان نیاز به رفتار خاصی با اعداد مانند دو دارد (مانند ۲ در ولزی، یا ۱۰۲ در اسلوونیایی).
fewوقتی زبان به رفتار خاصی با اعداد «کوچک» نیاز دارد (مانند ۲، ۳، و ۴ در زبان چکی؛ یا اعدادی که به ۲، ۳، یا ۴ ختم می‌شوند اما نه ۱۲، ۱۳، یا ۱۴ در زبان لهستانی).
manyوقتی زبان نیاز به رفتار خاصی با اعداد «بزرگ» دارد (مانند اعدادی که به ۱۱-۹۹ در زبان مالتی ختم می‌شوند).
otherوقتی زبان به رفتار خاصی برای کمیت داده‌شده نیاز ندارد (مانند همه اعداد در زبان چینی، یا ۴۲ در زبان انگلیسی).
مثال:

فایل XML در res/values/strings.xml ذخیره شد:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <plurals name="numberOfSongsAvailable">
        <!--
             As a developer, you should always supply "one" and "other"
             strings. Your translators will know which strings are actually
             needed for their language. Always include %d in "one" because
             translators will need to use %d for languages where "one"
             doesn't mean 1 (as explained above).
          -->
        <item quantity="one">%d song found.</item>
        <item quantity="other">%d songs found.</item>
    </plurals>
</resources>

فایل XML در res/values-pl/strings.xml ذخیره شد:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <plurals name="numberOfSongsAvailable">
        <item quantity="one">Znaleziono %d piosenkę.</item>
        <item quantity="few">Znaleziono %d piosenki.</item>
        <item quantity="other">Znaleziono %d piosenek.</item>
    </plurals>
</resources>

این کد برنامه یک رشته جمع را از داخل یک ترکیب‌پذیر با pluralStringResource() بازیابی می‌کند:

@Composable
fun SongCount(count: Int) {
    Text(
        text = pluralStringResource(
            R.plurals.numberOfSongsAvailable,
            count,
            count,
        )
    )
}

هنگام استفاده از تابع pluralStringResource()، اگر رشته شما شامل قالب‌بندی رشته با عدد باشد، باید count را دو بار ارسال کنید. برای مثال، برای رشته %d songs found، پارامتر count اول رشته جمع مناسب را انتخاب می‌کند و پارامتر count دوم در جای‌بان %d درج می‌شود. اگر رشته‌های جمع شما شامل قالب‌بندی رشته نباشد، نیازی به ارسال پارامتر سوم به pluralStringResource ندارید.

توجه: برای بازیابی رشته جمع خارج از تابع ترکیب‌پذیر، از context.resources.getQuantityString(R.plurals.numberOfSongsAvailable, count, count) استفاده کنید.

قالب و سبک

در اینجا چند نکته مهم درباره نحوه قالب‌بندی و سبک‌دهی صحیح منابع رشته‌ای آورده شده است.

نویسه‌های خاص نام کاربری

وقتی رشته‌ای حاوی نویسه‌هایی است که کاربرد خاصی در XML دارند، باید نویسه‌ها را طبق قوانین استاندارد گریز XML/HTML گریز دهید. اگر باید از نویسه‌ای که در Android معنای خاصی دارد استفاده کنید، باید از واکج‌خط قبل‌از آن استفاده کنید.

به‌طور پیش‌فرض، Android توالی نویسه‌های فاصله سفید را در یک فاصله تکی جمع می‌کند. با قرار دادن بخش مربوطه از رشته در گیومه دوتایی می‌توانید از این مشکل جلوگیری کنید. در این حالت، همه نویسه‌های فاصله سفید (ازجمله خطوط جدید) در منطقه نقل‌قول‌شده حفظ خواهند شد. علامت نقل‌قول دوتایی به شما امکان می‌دهد از علامت نقل‌قول تکی معمولی بدون گریز هم استفاده کنید.

نویسه فرم(های) گریز
@ \@
؟ \?
خط جدید \n
زبانه \t
نویسه یونی‌کد U+XXXX \uXXXX
علامت نقل قول تکی (')

هرکدام از موارد زیر:

  • \'
  • کل رشته را در علامت نقل‌قول دوتایی قرار دهید (برای مثال، "This'll work")
علامت نقل قول (") \"

توجه داشته باشید که قرار دادن رشته در گیومه تکی کار نمی‌کند.

فشرده‌سازی فضای خالی و گریز Android پس‌از تجزیه فایل منبع شما به‌عنوان XML اتفاق می‌افتد. این یعنی <string> &#32; &#8200; &#8195;</string> (فاصله، فاصله نقطه‌گذاری، فاصله Unicode Em) همگی به یک فاصله واحد (" ") تبدیل می‌شوند، زیرا پس‌از تجزیه فایل به‌عنوان XML، همگی فاصله‌های یونی‌کد هستند. برای حفظ این فاصله‌ها به همان صورتی که هستند، می‌توانید آن‌ها را نقل‌قول کنید (<string>" &#32; &#8200; &#8195;"</string>) یا از گریز Android استفاده کنید (<string> \u0032 \u8200 \u8195</string>).

درحال قالب‌بندی رشته‌ها

اگر لازم است رشته‌هایتان را قالب‌بندی کنید، می‌توانید این کار را با قرار دادن آرگومان‌های قالب‌بندی در منبع رشته انجام دهید، همان‌طور که در منبع نمونه زیر نشان داده شده است.

<string name="welcome_messages">Hello, %1$s! You have %2$d new messages.</string>

این کد برنامه رشته را از داخل یک ترکیب‌پذیر با ارسال آرگومان‌ها مستقیماً به stringResource() قالب‌بندی می‌کند:

@Composable
fun WelcomeMessage(username: String, mailCount: Int) {
    Text(
        text = stringResource(
            R.string.welcome_messages,
            username,
            mailCount,
        )
    )
}

سبک‌بندی با نشانه‌گذاری HTML

می‌توانید با نشانه‌گذاری HTML به رشته‌هایتان سبک اضافه کنید. برای مثال:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="welcome">Welcome to <b>Android</b>!</string>
</resources>

از عناصر HTML زیر پشتیبانی می‌شود:

  • پررنگ: <b>
  • ایتالیک: <i>، <cite>، <dfn>، <em>
  • نوشتار ۲۵٪ بزرگ‌تر: <big>
  • نوشتار ۲۰٪ کوچک‌تر: <small>
  • درحال تنظیم ویژگی‌های قلم: <font face="font_family" color="hex_color">. نمونه‌هایی از خانواده‌های قلم احتمالی شامل monospace، serif، و sans_serif است.
  • درحال تنظیم خانواده قلم تک‌فاصله: <tt>
  • خط روگذر: <s>، <strike>، <del>
  • زیرخط: <u>
  • بالانویس: <sup>
  • زیرنویس: <sub>
  • خالک‌ها: <ul>، <li>
  • شکست خط: <br>
  • بخش: <div>
  • سبک CSS: <span style="color|background_color|text-decoration">
  • پاراگراف‌ها: <p dir="rtl | ltr" style="…">

در برخی موارد، ممکن است بخواهید یک منبع متنی با سبک ایجاد کنید که همچنین به‌عنوان یک رشته قالب استفاده می‌شود. معمولاً این کار جواب نمی‌دهد زیرا روش‌های قالب‌بندی، مثل stringResource()، همه اطلاعات سبک را از رشته حذف می‌کنند. راه حل این مشکل این است که تگ‌های HTML را با موجودیت‌های گریز بنویسید، که سپس با AnnotatedString.fromHtml() بازیابی می‌شوند، پس‌از اینکه قالب‌بندی انجام شد. برای مثال:

  1. منبع نوشتار سبک‌دارتان را به‌عنوان رشته گریز HTML ذخیره کنید:
    <resources>
      <string name="welcome_messages">Hello, %1$s! You have &lt;b>%2$d new messages&lt;/b>.</string>
    </resources>

    در این رشته قالب‌بندی‌شده، عنصر <b> اضافه شده است. توجه داشته باشید که براکت باز بااستفاده از نماد &lt;، HTML-escaped است.

  2. سپس رشته را طبق معمول قالب‌بندی کنید، اما AnnotatedString.fromHtml() را نیز فراخوانی کنید تا نوشتار HTML را به رشته Compose دارای سبک تبدیل کنید.

چون fromHtml() همه واحدهای HTML را قالب‌بندی می‌کند، حتماً از هر نویسه HTML ممکن در رشته‌هایی که با نوشتار قالب‌بندی‌شده استفاده می‌کنید، بااستفاده از TextUtils.htmlEncode() بگریزید.

import android.text.TextUtils
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.fromHtml

@Composable
fun WelcomeHtmlMessage(username: String, mailCount: Int) {
    // Escape the username in case it contains characters like "<" or "&"
    val escapedUsername = TextUtils.htmlEncode(username)

    val text = stringResource(
        R.string.welcome_messages,
        escapedUsername,
        mailCount,
    )

    Text(
        text = AnnotatedString.fromHtml(text)
    )
}

سبک‌پردازی با AnnotatedString

AnnotatedString یک شیء نوشتاری Compose است که می‌توانید با ویژگی‌هایی مثل رنگ و وزن قلم به آن سبک دهید. ساختن نوشتار سبک‌دار به‌صورت برنامه‌نویسی‌شده بااستفاده از buildAnnotatedString و withStyle.

این کد برنامه یک عنصر نوشتاری واحد با سبک‌های ترکیبی ایجاد می‌کند:

@Composable
fun StyledGreeting() {
    val styled = buildAnnotatedString {
        append("Welcome to ")
        withStyle(SpanStyle(fontWeight = FontWeight.Bold)) {
            append("Android")
        }
        append("!")
    }
    Text(text = styled)
}

برای اعمال رنگ، اندازه قلم، و تزئین نوشتار، از SpanStyle استفاده کنید. برای اعمال کردن سبک‌بندی سطح پاراگراف (مانند تراز یا ارتفاع خط)، از ParagraphStyle استفاده کنید:

@Composable
fun RichText() {
    val text = buildAnnotatedString {
        withStyle(ParagraphStyle(lineHeight = 24.sp, textAlign = TextAlign.Center)) {
            withStyle(SpanStyle(color = Color.Gray)) {
                append("Hello, ")
            }
            withStyle(
                SpanStyle(
                    fontWeight = FontWeight.Bold,
                    color = Color.Red,
                )
            ) {
                append("world")
            }
            append("!")
        }
    }
    Text(text = text)
}

ساختن مستقیم AnnotatedString رویکرد توصیه‌شده برای برنامه‌های تک‌زبانه یا نوشتار ایستا در Compose است. بااین‌حال، برای نوشتار سبک‌دار که به بومی‌سازی نیاز دارد، رویکرد XML <annotation> را که در بخش بعدی به‌تفصیل شرح داده شده است ببینید.

سبک‌بندی رشته‌های ترجمه‌شده با حاشیه‌نویسی

برای رشته‌هایی که به سبک سفارشی و ترجمه نیاز دارند، برچسب <annotation> را در strings.xml هر منطقه زبانی تعریف کنید. مترجمان حاشیه‌نویسی را صرف‌نظر از اینکه در کجای جمله قرار می‌گیرد حفظ می‌کنند. رشته را با context.resources.getText() بخوانید، بازه‌های Annotation آن را پیمایش کنید، و نتیجه را به AnnotatedString تبدیل کنید:

@Composable
fun AnnotatedTitle() {
    val context = LocalContext.current
    val source = context.resources.getText(R.string.title) as SpannedString
    val text = buildAnnotatedString {
        append(source.toString())
        source.getSpans(0, source.length, Annotation::class.java)
            .forEach { annotation ->
                if (annotation.key == "font" &&
                    annotation.value == "title_emphasis") {
                    addStyle(
                        SpanStyle(
                            fontFamily = FontFamily(
                                Font(R.font.permanent_marker)
                            )
                        ),
                        source.getSpanStart(annotation),
                        source.getSpanEnd(annotation),
                    )
                }
            }
    }
    Text(text = text)
}

برچسب <annotation> در XML شما تغییر نکرده است. فقط کد بازیابی متفاوت است. مترجمان همچنان برچسب را جابه‌جا می‌کنند تا کلمه صحیح را در هر زبان دربرگیرد.

منابع بیشتر

برای اطلاعات بیشتر درباره منابع رشته، منابع اضافی زیر را ببینید:

اسناد

محتوا را مشاهده می‌کند