Ciągi tekstowe

Zasób tekstowy zawiera ciągi tekstowe dla aplikacji z opcjonalnym stylem i formatowaniem tekstu. Istnieją 3 rodzaje zasobów, które mogą dostarczać aplikacji ciągi znaków:

String
Zasób XML, który zawiera pojedynczy ciąg znaków.
Tablica ciągów znaków
Zasób XML, który zawiera tablicę ciągów znaków.
Ciągi znaków ilości (liczba mnoga)
Plik zasobu XML zawierający różne ciągi znaków do tworzenia form liczby mnogiej.

Wszystkie ciągi tekstowe mogą stosować pewne znaczniki stylów i argumenty formatowania. Informacje o stylizowaniu i formatowaniu ciągów znaków znajdziesz w sekcji Formatowanie i stylizowanie.

Ciąg znaków

Pojedynczy ciąg znaków, do którego można się odwoływać w kodzie aplikacji (np. w funkcji typu „composable”) lub w innych plikach zasobów.

lokalizacja pliku:
res/values/filename.xml
Nazwa pliku jest dowolna. Element <string> z atrybutem name jest używany jako identyfikator zasobu.
skompilowany typ danych zasobu:
Wskaźnik zasobu do String.
odwołanie do zasobu:
W języku Kotlin: R.string.string_name
W języku XML: @string/string_name
składnia:
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string
        name="string_name"
        >text_string</string>
</resources>
elements:
<resources>
Wymagane. Musi to być węzeł główny.

Brak atrybutów.

<string>
Ciąg tekstowy, który może zawierać tagi stylów. Pamiętaj, że musisz używać apostrofów i cudzysłowów jako znaków modyfikacji. Więcej informacji o prawidłowym formatowaniu ciągów znaków znajdziesz w sekcji Formatowanie i stylizacja poniżej.

atrybuty:

name
Ciąg znaków Nazwa ciągu znaków. Ta nazwa jest używana jako identyfikator zasobu.
przykład:
Plik XML zapisany w lokalizacji res/values/strings.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="hello">Hello!</string>
</resources>

Ten kod aplikacji pobiera ciąg znaków z funkcji kompozycyjnej za pomocą stringResource():

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

Uwaga: aby pobrać ciąg znaków poza funkcją typu „composable”, użyj funkcji context.getString(R.string.hello).

Możesz też odwoływać się do zasobów ciągów znaków z innych plików XML, np. z pliku AndroidManifest.xml:
<activity
    android:name=".MainActivity"
    android:label="@string/hello" />

Tablica tekstowa

Tablica ciągów tekstowych, do których można się odwoływać z aplikacji.

lokalizacja pliku:
res/values/filename.xml
Nazwa pliku jest dowolna. Element <string-array> z atrybutem name jest używany jako identyfikator zasobu.
skompilowany typ danych zasobu:
Wskaźnik zasobu do tablicy elementów String.
odwołanie do zasobu:
W języku Kotlin: R.array.string_array_name
W języku XML: @[package:]array/string_array_name
składnia:
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string-array
        name="string_array_name">
        <item
            >text_string</item>
    </string-array>
</resources>
elements:
<resources>
Wymagane. Musi to być węzeł główny.

Brak atrybutów.

<string-array>
Definiuje tablicę ciągów znaków. Zawiera co najmniej 1 element <item>.

atrybuty:

name
Ciąg znaków Nazwa tablicy. Ta nazwa jest używana jako identyfikator zasobu do odwoływania się do tablicy.
<item>
Ciąg tekstowy, który może zawierać tagi stylów. Wartość może być odwołaniem do innego zasobu tekstowego. Musi być elementem podrzędnym elementu <string-array>. Pamiętaj, że musisz użyć znaku zmiany znaczenia apostrofu i cudzysłowu. Więcej informacji o prawidłowym formatowaniu ciągów znaków znajdziesz w sekcji Formatowanie i style poniżej.

Brak atrybutów.

przykład:
Plik XML zapisany w lokalizacji 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>

Ten kod aplikacji pobiera tablicę tekstową z funkcji kompozycyjnej za pomocą funkcji stringArrayResource():

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

Uwaga: aby pobrać tablicę tekstową poza funkcją typu „composable”, użyj funkcji context.resources.getStringArray(R.array.planets_array).

Ciągi ilościowe (liczba mnoga)

W różnych językach obowiązują różne zasady dotyczące zgodności gramatycznej z liczbą. Na przykład w języku angielskim liczba 1 jest przypadkiem szczególnym. Piszesz „1 książka”, ale w przypadku innych ilości piszesz „n książek”. To rozróżnienie między liczbą pojedynczą a mnogą jest bardzo powszechne, ale w innych językach występuje więcej rozróżnień. Pełny zestaw obsługiwany przez Androida to zero, one, two, few, manyother.

Reguły decydujące o tym, którego przypadku użyć w danym języku i dla danej ilości, mogą być bardzo złożone, dlatego Android udostępnia metody takie jak pluralStringResource(), które pozwalają wybrać odpowiedni zasób.

Chociaż historycznie ciągi ilościowe były nazywane „ciągami ilości” (i tak są nadal nazywane w interfejsie API), powinny być używane tylko w przypadku liczby mnogiej. Nie należy używać ciągów ilościowych do implementowania takich funkcji jak „Skrzynka odbiorcza” w Gmailu i „Skrzynka odbiorcza (12)” w przypadku nieprzeczytanych wiadomości. Używanie ciągów ilościowych zamiast instrukcji if może się wydawać wygodne, ale warto pamiętać, że w niektórych językach (np. w chińskim) takie rozróżnienia gramatyczne nie występują, więc zawsze otrzymasz ciąg other.

Wybór ciągu znaków zależy wyłącznie od konieczności gramatycznej. W języku angielskim ciąg znaków dla zero jest ignorowany, nawet jeśli ilość jest mniejsza niż 0, ponieważ 0 nie różni się gramatycznie od 2 ani od żadnej innej liczby z wyjątkiem 1 („zero books”, „one book”, „two books” itd.). Z kolei w przypadku języka koreańskiego wyłącznie używany jest ciąg znaków other.

Nie daj się też zwieść temu, że np. two brzmi tak, jakby odnosiło się tylko do liczby 2: w danym języku może być wymagane, aby liczby 2, 12, 102 (i tak dalej) były traktowane w ten sam sposób, ale inaczej niż inne liczby. Zaufaj tłumaczowi, który wie, jakie rozróżnienia są ważne w jego języku.

Jeśli wiadomość nie zawiera liczby, prawdopodobnie nie jest dobrym kandydatem do liczby mnogiej. Na przykład w języku litewskim forma liczby pojedynczej jest używana zarówno w przypadku liczby 1, jak i 101, więc „1 książka” jest tłumaczona jako „1 knyga”, a „101 książek” jako „101 knyga”. „a book” to „knyga”, a „many books” to „daug knygų”. Jeśli komunikat w języku angielskim w liczbie mnogiej zawiera „a book” (liczba pojedyncza) i „many books” (liczba mnoga) bez podania konkretnej liczby, można go przetłumaczyć jako „knyga” (książka) lub „daug knygų” (wiele książek), ale zgodnie z zasadami języka litewskiego w przypadku liczby 101 wyświetli się „knyga” (jedna książka).

Często można uniknąć ciągów znaków ilościowych, używając sformułowań neutralnych pod względem ilości, np. „Książki: 1”. Ułatwi to pracę Tobie i tłumaczom, jeśli jest to akceptowalny styl w przypadku Twojej aplikacji.

W przypadku interfejsu API w wersji 24 lub nowszej możesz zamiast tego użyć znacznie bardziej zaawansowanej klasy ICU MessageFormat.

lokalizacja pliku:
res/values/filename.xml
Nazwa pliku jest dowolna. Element <plurals> z atrybutem name jest używany jako identyfikator zasobu.
odwołanie do zasobu:
W Kotlinie: R.plurals.plural_name
składnia:
<?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>
Wymagane. Musi to być węzeł główny.

Brak atrybutów.

<plurals>
Zbiór ciągów znaków, z których jeden jest podawany w zależności od ilości czegoś. Zawiera co najmniej 1 element <item>.

atrybuty:

name
Ciąg znaków Nazwa pary ciągów znaków. Ta nazwa jest używana jako identyfikator zasobu.
<item>
Ciąg w liczbie mnogiej lub pojedynczej. Wartość może być odwołaniem do innego zasobu tekstowego. Musi być elementem podrzędnym elementu <plurals>. Pamiętaj, że musisz zmienić znaczenie apostrofów i cudzysłowów. Więcej informacji o prawidłowym formatowaniu ciągów znaków znajdziesz w sekcji Formatowanie i style poniżej.

atrybuty:

quantity
Słowo kluczowe Wartość wskazująca, kiedy należy użyć tego ciągu tekstowego. Prawidłowe wartości (przykłady w nawiasach):
WartośćOpis
zeroGdy język wymaga specjalnego traktowania liczby 0 (np. w języku arabskim).
oneGdy język wymaga specjalnego traktowania liczb, np. jeden (jak w przypadku liczby 1 w języku angielskim i większości innych języków; w języku rosyjskim do tej klasy należy każda liczba kończąca się na 1, ale nie na 11).
twoGdy język wymaga specjalnego traktowania liczb, np. dwóch (jak w przypadku liczby 2 w języku walijskim lub 102 w języku słoweńskim).
fewGdy język wymaga specjalnego traktowania „małych” liczb (np. 2, 3 i 4 w języku czeskim lub liczb kończących się na 2, 3 lub 4, ale nie 12, 13 lub 14 w języku polskim).
manyGdy język wymaga specjalnego traktowania „dużych” liczb (np. w przypadku liczb kończących się na 11–99 w języku maltańskim).
otherGdy język nie wymaga specjalnego traktowania danej ilości (jak w przypadku wszystkich liczb w języku chińskim lub liczby 42 w języku angielskim).
przykład:

Plik XML zapisany w lokalizacji 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>

Plik XML zapisany w lokalizacji 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>

Ten kod aplikacji pobiera ciąg znaków w liczbie mnogiej z funkcji kompozycyjnej za pomocą funkcji pluralStringResource():

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

Jeśli używasz funkcji pluralStringResource(), musisz przekazać wartość count 2 razy, jeśli ciąg znaków zawiera formatowanie ciągu znaków z liczbą. Na przykład w przypadku ciągu znaków %d songs found pierwszy parametr count wybiera odpowiedni ciąg znaków w liczbie mnogiej, a drugi parametr count jest wstawiany w miejsce symbolu zastępczego %d. Jeśli ciągi w liczbie mnogiej nie zawierają formatowania, nie musisz przekazywać trzeciego parametru do funkcji pluralStringResource.

Uwaga: aby pobrać ciąg w liczbie mnogiej poza funkcją typu „composable”, użyj funkcji context.resources.getQuantityString(R.plurals.numberOfSongsAvailable, count, count).

Format i styl

Oto kilka ważnych informacji o prawidłowym formatowaniu i stylizowaniu zasobów w postaci ciągów znaków.

Obsługa znaków specjalnych

Jeśli ciąg znaków zawiera znaki, które mają specjalne zastosowanie w XML, musisz je zmienić zgodnie ze standardowymi regułami zmiany znaków w XML/HTML. Jeśli musisz wskazać zmianę znaczenia znaku, który ma specjalne znaczenie w Androidzie, użyj poprzedzającego go lewego ukośnika.

Domyślnie Android zwija sekwencje znaków odstępu do pojedynczej spacji. Aby tego uniknąć, umieść odpowiednią część ciągu znaków w cudzysłowie. W tym przypadku wszystkie znaki odstępu (w tym nowe wiersze) zostaną zachowane w cytowanym regionie. Podwójne cudzysłowy umożliwiają też używanie zwykłych pojedynczych cudzysłowów niepoprzedzonych znakiem zmiany znaczenia.

Znak Formularze z odpowiednio zmienionymi znakami
@ \@
? \?
Nowy wiersz \n
Tab \t
Znak Unicode U+XXXX \uXXXX
Pojedynczy cudzysłów (')

Dowolna z tych wartości:

  • \'
  • Umieść cały ciąg znaków w cudzysłowie podwójnym (np. "This'll work").
Podwójny cudzysłów (") \"

Pamiętaj, że umieszczenie ciągu znaków w pojedynczych cudzysłowach nie działa.

Zwijanie białych znaków i ucieczka znaków w Androidzie następują po przeanalizowaniu pliku zasobów jako XML. Oznacza to, że znaki <string> &#32; &#8200; &#8195;</string> (spacja, spacja interpunkcyjna, spacja Unicode Em) są spłaszczane do pojedynczej spacji (" "), ponieważ po przeanalizowaniu pliku jako XML wszystkie są traktowane jako spacje Unicode. Aby zachować te spacje w niezmienionej formie, możesz je ująć w cudzysłów (<string>" &#32; &#8200; &#8195;"</string>) lub użyć znaków ucieczki w Androidzie (<string> \u0032 \u8200 \u8195</string>).

Ciągi formatujące

Jeśli chcesz sformatować ciągi znaków, możesz to zrobić, umieszczając argumenty formatu w zasobie tekstowym, jak pokazano w tym przykładzie zasobu:

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

Ten kod aplikacji formatuje ciąg znaków w funkcji kompozycyjnej, przekazując argumenty bezpośrednio do funkcji stringResource():

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

Stylizowanie za pomocą znaczników HTML

Do ciągów znaków możesz dodawać style za pomocą znaczników HTML. Przykład:

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

Obsługiwane są te elementy HTML:

  • Pogrubienie: <b>
  • Kursywa: <i>, <cite>, <dfn>, <em>
  • Tekst o 25% większy: <big>
  • Tekst o 20% mniejszy: <small>
  • Ustawianie właściwości czcionki: <font face="font_family" color="hex_color">. Przykłady możliwych rodzin czcionek to monospace, serifsans_serif.
  • Ustawianie rodziny czcionek o stałej szerokości: <tt>
  • Przekreślenie: <s>, <strike>, <del>
  • Podkreślenie: <u>
  • Indeks górny: <sup>
  • Indeks dolny: <sub>
  • Lista punktowana: <ul>, <li>
  • Podziały wiersza: <br>
  • Oddział: <div>
  • Styl CSS: <span style="color|background_color|text-decoration">
  • Akapity: <p dir="rtl | ltr" style="…">

W niektórych przypadkach możesz utworzyć zasób tekstowy ze stylem, który będzie też używany jako ciąg formatujący. Zwykle nie działa to, ponieważ metody formatowania, takie jak stringResource(), usuwają z ciągu wszystkie informacje o stylu. Obejście tego problemu polega na zapisaniu tagów HTML z użyciem znaków specjalnych, które są następnie odzyskiwane za pomocą AnnotatedString.fromHtml() po sformatowaniu. Przykład:

  1. Zapisz zasób tekstu ze stylami jako ciąg tekstowy z kodowaniem HTML:
    <resources>
      <string name="welcome_messages">Hello, %1$s! You have &lt;b>%2$d new messages&lt;/b>.</string>
    </resources>

    Do tego sformatowanego ciągu dodawany jest element <b>. Zwróć uwagę, że nawias otwierający jest zakodowany w HTML-u za pomocą notacji &lt;.

  2. Następnie sformatuj ciąg znaków jak zwykle, ale wywołaj też funkcję AnnotatedString.fromHtml(), aby przekonwertować tekst HTML na sformatowany ciąg znaków Compose.

Ponieważ funkcja fromHtml() formatuje wszystkie encje HTML, pamiętaj, aby w ciągach używanych z sformatowanym tekstem zmienić znaczenie wszystkich możliwych znaków HTML za pomocą funkcji 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)
    )
}

Stylizowanie za pomocą klasy AnnotatedString

AnnotatedString to obiekt tekstowy Compose, który możesz stylizować za pomocą właściwości takich jak kolor i grubość czcionki. Twórz sformatowany tekst programowo za pomocą buildAnnotatedStringwithStyle.

Ten kod aplikacji tworzy jeden element tekstowy z mieszanymi stylami:

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

Aby zastosować kolor, rozmiar czcionki i dekorację tekstu, użyj SpanStyle. Aby zastosować styl na poziomie akapitu (np. wyrównanie lub wysokość wiersza), użyj 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)
}

Bezpośrednie tworzenie AnnotatedString to zalecane podejście w przypadku aplikacji w jednym języku lub statycznego tekstu w Compose. Jeśli jednak chodzi o sformatowany tekst, który wymaga lokalizacji, zapoznaj się z podejściem opartym na XML <annotation> opisanym w następnej sekcji.

Stylizowanie przetłumaczonych ciągów znaków za pomocą adnotacji

W przypadku ciągów znaków, które wymagają niestandardowego formatowania i tłumaczenia, zdefiniuj tag <annotation> w pliku strings.xml każdego języka. Tłumacze zachowują adnotację niezależnie od tego, gdzie znajduje się ona w zdaniu. Odczytaj ciąg znaków za pomocą funkcji context.resources.getText(), przejdź przez jego zakresy Annotation i przekonwertuj wynik na 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)
}

Tag <annotation> w pliku XML pozostaje bez zmian. Różni się tylko kod odzyskiwania. Tłumacze nadal przenoszą tag, aby otaczał on odpowiednie słowo w każdym języku.

Dodatkowe materiały

Więcej informacji o zasobach tekstowych znajdziesz w tych materiałach:

Dokumentacja

Wyświetla treści