משאבי מחרוזת

משאב מחרוזת מספק מחרוזות טקסט לאפליקציה עם עיצוב ופורמט טקסט אופציונליים. יש שלושה סוגים של מקורות שיכולים לספק מחרוזות לאפליקציה:

מחרוזת
משאב XML שמספק מחרוזת אחת.
מערך מחרוזות
משאב XML שמספק מערך של מחרוזות.
Quantity Strings (Plurals)
משאב 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>
elements:‎
<resources>
חובה. זה חייב להיות צומת הבסיס.

אין מאפיינים.

<string>
מחרוזת, שיכולה לכלול תגי סגנון. חשוב לזכור שצריך להוסיף תווי בריחה (escape) לגרשיים ולמירכאות. מידע נוסף על עיצוב נכון של מחרוזות מופיע בקטע עיצוב בהמשך.

מאפיינים:

name
String. שם המחרוזת. השם הזה משמש כמזהה המשאב.
דוגמא:
קובץ ה-XML ‏
נשמר במיקום res/values/strings.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="hello">Hello!</string>
</resources>

קוד האפליקציה הזה מאחזר מחרוזת מתוך פונקציה שאפשר להשתמש בה עם 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>
elements:‎
<resources>
חובה. זה חייב להיות צומת הבסיס.

אין מאפיינים.

<string-array>
מגדיר מערך של מחרוזות. מכיל רכיב <item> אחד או יותר.

מאפיינים:

name
String. שם המערך. השם הזה משמש כמזהה המשאב כדי להפנות למערך.
<item>
מחרוזת, שיכולה לכלול תגי סגנון. הערך יכול להיות הפניה למשאב מחרוזת אחר. חייב להיות רכיב צאצא של רכיב <string-array>. חשוב לזכור שצריך להוסיף תווי בריחה (escape) לגרשיים ולמירכאות. בקטע עיצוב וסגנון שבהמשך מוסבר איך לעצב את המחרוזות בפורמט הנכון.

אין מאפיינים.

דוגמא:
קובץ ה-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).

מחרוזות של כמות (צורות רבות)

בשפות שונות יש כללים שונים להתאמה דקדוקית לכמות. לדוגמה, באנגלית, הכמות 1 היא מקרה מיוחד. אנחנו כותבים 'ספר אחד', אבל אם מדובר בכמות אחרת, אנחנו כותבים 'n ספרים'. ההבחנה הזו בין יחיד לרבים היא מאוד נפוצה, אבל בשפות אחרות יש הבחנות מדויקות יותר. הקבוצה המלאה שנתמכת ב-Android היא zero,‏ one,‏ two,‏ few,‏ many ו-other.

הכללים להחלטה באיזה מקרה להשתמש עבור שפה וכמות מסוימות יכולים להיות מורכבים מאוד, ולכן Android מספקת שיטות כמו pluralStringResource() כדי לבחור את המשאב המתאים.

למרות שבעבר הם נקראו 'מחרוזות כמות' (והם עדיין נקראים כך ב-API), צריך להשתמש במחרוזות כמות רק עבור צורות רבות. לדוגמה, לא מומלץ להשתמש במחרוזות של כמויות כדי להטמיע משהו כמו 'תיבת דואר נכנס' לעומת 'תיבת דואר נכנס (12)' ב-Gmail, כשקיימות הודעות שלא נקראו. יכול להיות שיהיה לכם נוח להשתמש במחרוזות של כמות במקום בהצהרת if, אבל חשוב לזכור שבחלק מהשפות (כמו סינית) אין הבחנות דקדוקיות כאלה בכלל, ולכן תמיד תקבלו את המחרוזת other.

הבחירה באיזה מחרוזת להשתמש מתבצעת אך ורק על סמך הצורך הדקדוקי. באנגלית, מחרוזת של zero מוזנחת גם אם הכמות היא 0, כי מבחינה דקדוקית 0 לא שונה מ-2 או מכל מספר אחר חוץ מ-1 (למשל, zero books,‏ one book,‏ two books וכן הלאה). לעומת זאת, בקוריאה בלבד נעשה שימוש רק במחרוזת other.

אל תתבלבלו גם מהעובדה שאולי נראה לכם שהביטוי two מתאים רק לכמות 2: יכול להיות שבשפה מסוימת צריך להתייחס ל-2, ל-12, ל-102 (וכך הלאה) באופן זהה, אבל שונה מכמויות אחרות. כדאי להסתמך על המתרגם כדי לדעת אילו הבחנות השפה שלו דורשת.

אם ההודעה לא מכילה את מספר הכמות, סביר להניח שהיא לא מתאימה לריבוי. לדוגמה, בליטאית משתמשים בצורת היחיד גם עבור 1 וגם עבור 101, ולכן 'ספר אחד' מתורגם ל-'1 knyga', ו-'101 ספרים' מתורגם ל-'101 knyga'. בינתיים, 'ספר' הוא 'knyga' ו'הרבה ספרים' הם 'daug knygų'. אם הודעה באנגלית ברבים מכילה את המילים a book (ספר אחד) ו-many books (הרבה ספרים) בלי המספר בפועל, אפשר לתרגם אותה ל-knyga (ספר אחד) או ל-daug knygų (הרבה ספרים), אבל לפי הכללים בליטאית, אם המספר הוא 101, יוצג knyga (ספר אחד).

לפעמים אפשר להימנע ממחרוזות של כמויות באמצעות ניסוחים שלא תלויים בכמות, כמו "ספרים: 1". כך יהיה לכם ולמתרגמים שלכם קל יותר, אם זה סגנון מקובל לאפליקציה שלכם.

ב-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>
elements:‎
<resources>
חובה. זה חייב להיות צומת הבסיס.

אין מאפיינים.

<plurals>
אוסף של מחרוזות, שמתוכו מסופקת מחרוזת אחת בהתאם לכמות של משהו. מכיל רכיב <item> אחד או יותר.

מאפיינים:

name
String. שם לצמד המחרוזות. השם הזה משמש כמזהה המשאב.
<item>
מחרוזת ברבים או ביחיד. הערך יכול להיות הפניה למשאב מחרוזת אחר. חייב להיות רכיב צאצא של רכיב <plurals>. חשוב לזכור שצריך להוסיף תווי בריחה (escape) לגרשיים ולמירכאות. בקטע עיצוב וסגנון שבהמשך מוסבר איך לעצב את המחרוזות בפורמט הנכון.

מאפיינים:

quantity
מילת מפתח. ערך שמציין מתי צריך להשתמש במחרוזת הזו. ערכים תקינים, עם דוגמאות (רשימה חלקית) בסוגריים:
ערךתיאור
zeroכשהשפה דורשת טיפול מיוחד במספר 0 (כמו בערבית).
oneכשהשפה דורשת טיפול מיוחד במספרים כמו אחד (כמו המספר 1 באנגלית וברוב השפות האחרות; ברוסית, כל מספר שמסתיים ב-1 אבל לא מסתיים ב-11 שייך לסיווג הזה).
twoכשהשפה דורשת טיפול מיוחד במספרים כמו 2 (כמו ב-2 בוולשית או ב-102 בסלובנית).
fewכשהשפה דורשת טיפול מיוחד במספרים 'קטנים' (כמו 2, 3 ו-4 בצ'כית, או מספרים שמסתיימים ב-2, 3 או 4 אבל לא ב-12, 13 או 14 בפולנית).
manyכשבשפה נדרש טיפול מיוחד במספרים 'גדולים' (כמו במספרים שמסתיימים ב-11 עד 99 במלטזית).
otherכשהשפה לא דורשת טיפול מיוחד בכמות הנתונה (כמו כל המספרים בסינית, או 42 באנגלית).
דוגמא:

קובץ ה-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, צריך להוסיף לתווים escape בהתאם לכללי ה-escape הסטנדרטיים של XML/HTML. אם אתם צריכים להשתמש בתו בריחה לתו שיש לו משמעות מיוחדת ב-Android, אתם צריכים להוסיף לפניו קו נטוי הפוך.

כברירת מחדל, מערכת Android מכווצת רצפים של תווים שיוצרים רווח לרווח בודד. כדי להימנע מכך, צריך להוסיף מירכאות כפולות סביב החלק הרלוונטי במחרוזת. במקרה כזה, כל תווי הרווח הלבן (כולל שורות חדשות) יישמרו בתוך האזור שמוקף במירכאות. השימוש במירכאות כפולות מאפשר גם שימוש במירכאות רגילות בודדות שלא כוללות תווי escape.

דמות טפסים שהושלמו
@ \@
? \?
שורה חדשה \n
Tab \t
תו Unicode ‏U+XXXX \uXXXX
גרש (')

כל אחת מהאפשרויות הבאות:

  • \'
  • תוחמים את כל המחרוזת במירכאות כפולות (לדוגמה, "This'll work")
מירכאות כפולות (") \"

שימו לב: אי אפשר להקיף את המחרוזת במירכאות בודדות.

הצמצום של הרווחים הלבנים וההסרה של התווים המיוחדים ב-Android מתבצעים אחרי שקובץ המשאבים עובר ניתוח כ-XML. המשמעות היא שכל הערכים <string> &#32; &#8200; &#8195;</string> (רווח, רווח של סימן פיסוק, רווח Em של Unicode) מצטמצמים לרווח אחד (" "), כי כולם רווחים של Unicode אחרי שהקובץ מנותח כ-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>
  • טקסט גדול ב-25%: <big>
  • טקסט קטן ב-20%: <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 עם ישויות שעברו escape, ואז לשחזר אותן באמצעות AnnotatedString.fromHtml() אחרי העיצוב. לדוגמה:

  1. מאחסנים את משאב הטקסט עם העיצוב כמחרוזת עם תווי escape של HTML:
    <resources>
      <string name="welcome_messages">Hello, %1$s! You have &lt;b>%2$d new messages&lt;/b>.</string>
    </resources>

    במחרוזת המעוצבת הזו, נוסף רכיב <b>. שימו לב שהסוגר הפותח הוא HTML-escaped, באמצעות הסימון &lt;.

  2. לאחר מכן מעצבים את המחרוזת כרגיל, אבל גם קוראים ל-AnnotatedString.fromHtml() כדי להמיר את טקסט ה-HTML למחרוזת Compose עם סגנון.

הפונקציה fromHtml() מעצבת את כל ישויות ה-HTML, לכן חשוב להשתמש ב-TextUtils.htmlEncode() כדי להוסיף תווי בריחה לכל תווי ה-HTML האפשריים במחרוזות שבהן משתמשים עם הטקסט המעוצב.

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 לא משתנה. רק קוד האחזור שונה. המתרגמים עדיין מעבירים את התג כדי להקיף את המילה הנכונה בכל שפה.

מקורות מידע נוספים

למידע נוסף על משאבי מחרוזות, אפשר לעיין במקורות המידע הנוספים הבאים:

מאמרי עזרה

צפייה בתוכן