Строковые ресурсы

Строковый ресурс предоставляет текстовые строки для вашего приложения с дополнительным стилем и форматированием текста. Существует три типа ресурсов, которые могут предоставлять вашему приложению строки:

String
XML-ресурс, который предоставляет одну строку.
Массив строк
Ресурс XML, который предоставляет массив строк.
Строки с количеством (множественное число)
XML-ресурс, содержащий разные строки для множественного числа.

Все строки могут содержать разметку стилей и аргументы форматирования. Подробнее о форматировании и стилизации строк…

Строка

Одна строка, на которую можно ссылаться из кода приложения (например, из composable-функции) или из других файлов ресурсов.

местоположение файла;
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>
Строка, которая может включать теги стилей. Обратите внимание, что апострофы и кавычки необходимо экранировать. Подробнее о форматировании и стилях строк…

атрибуты:

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))
}

Примечание. Чтобы получить строку за пределами composable-функции, используйте 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
Строка. Название массива. Это название используется в качестве идентификатора ресурса для ссылки на массив.
<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>

Этот код приложения извлекает массив строк из composable-функции с помощью stringArrayResource():

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

Примечание. Чтобы получить массив строк за пределами composable-функции, используйте context.resources.getStringArray(R.array.planets_array).

Строки с количеством (множественное число)

В разных языках действуют разные правила грамматического согласования с количеством. Например, в английском языке количество 1 – это особый случай. Мы пишем "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 книга" переводится как "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
Строка. Название пары строк. Это название используется в качестве идентификатора ресурса.
<item>
Строка во множественном или единственном числе. Значение может быть ссылкой на другой строковый ресурс. Должен быть дочерним элементом <plurals>. Обратите внимание, что апострофы и кавычки необходимо экранировать. Подробнее о том, как правильно оформлять и форматировать строки, рассказывается в разделе Форматирование и стили.

атрибуты:

quantity
Ключевое слово. Значение, указывающее, когда следует использовать эту строку. Допустимые значения (в скобках приведены неполные примеры):
ЗначениеОписание
zeroКогда в языке требуется особое обращение с числом 0 (как в арабском).
oneИспользуется, когда в языке требуется особое обращение с числами, похожими на единицу (например, с числом 1 в английском и большинстве других языков; в русском языке к этому классу относится любое число, оканчивающееся на 1, но не на 11).
twoКогда в языке требуется особое обращение с числами, такими как два (например, 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>

Этот код приложения извлекает строку во множественном числе из composable-функции с помощью pluralStringResource():

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

При использовании функции pluralStringResource() вам нужно передать count дважды, если ваша строка включает форматирование строки с числом. Например, для строки %d songs found первый параметр count выбирает подходящую строку во множественном числе, а второй параметр count вставляется в плейсхолдер %d. Если строки во множественном числе не содержат форматирования, передавать третий параметр в pluralStringResource не нужно.

Примечание. Чтобы получить строку во множественном числе за пределами composable-функции, используйте context.resources.getQuantityString(R.plurals.numberOfSongsAvailable, count, count).

Формат и стиль

Ниже приведены важные сведения о том, как правильно форматировать и оформлять строковые ресурсы.

Как обрабатывать специальные символы

Если строка содержит символы, которые имеют специальное назначение в XML, их необходимо экранировать в соответствии со стандартными правилами экранирования XML/HTML. Если вам нужно экранировать символ, имеющий особое значение в Android, используйте перед ним обратную косую черту.

По умолчанию Android объединяет последовательности пробельных символов в один пробел. Чтобы избежать этого, заключите нужную часть строки в двойные кавычки. В этом случае все пробелы (включая символы новой строки) будут сохранены в области, заключенной в кавычки. Двойные кавычки позволят вам использовать обычные одинарные неэкранированные кавычки.

Персонаж Формы с экранированием
@ \@
? \?
Новая строка \n
Tab \t
Символ Unicode U+XXXX \uXXXX
Одинарная кавычка (')

Любой из следующих вариантов:

  • \'
  • Заключите всю строку в двойные кавычки (например, "This'll work").
Двойная кавычка (") \"

Обратите внимание, что одинарные кавычки не поддерживаются.

Удаление пробелов и экранирование символов Android происходят после того, как файл ресурсов будет обработан как XML. Это означает, что <string> &#32; &#8200; &#8195;</string> (пробел, пробел после знака препинания, круглая шпация 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>

Этот код приложения форматирует строку из composable-функции, передавая аргументы напрямую в 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 экранированные символы, которые затем восстанавливаются с помощью 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;.

  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-файле не изменен. Различаются только коды получения. Переводчики по-прежнему перемещают тег, чтобы выделить нужное слово на каждом языке.

Дополнительные ресурсы

Дополнительную информацию о строковых ресурсах можно найти в следующих источниках:

Документация

Просмотр контента