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

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

Нить
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>

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

Последовательность чисел (во множественном числе)

В разных языках существуют разные правила грамматического согласования с количеством. Например, в английском языке количество 1 является особым случаем. Мы пишем "1 книга", но для любого другого количества мы бы написали " n книг". Это различие между единственным и множественным числом очень распространено, но в других языках существуют более тонкие различия. Полный набор слов, поддерживаемых Android, включает zero , one , two , few , many и other .

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

Хотя исторически их называли «строками количества» (и до сих пор так называют в API), строки количества следует использовать только для множественного числа. Было бы ошибкой использовать строки количества для реализации чего-то вроде «Входящие» в Gmail и «Входящие (12)», когда есть непрочитанные сообщения, например. Может показаться удобным использовать строки количества вместо оператора 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ų». Если в английском сообщении во множественном числе содержатся «книга» (единственное число) и «много книг» (множественное число) без указания фактического количества, его можно перевести как «knyga» (книга)/«daug knygų» (много книг), но по литовским правилам будет указано «knyga» (одна книга), если количество равно 101.

Часто можно избежать использования количественных строк, применяя нейтральные к количеству формулировки, например, "Книги: 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>
элементы:
<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>

Данный код приложения извлекает строку во множественном числе из составного объекта с помощью 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 все они являются пробелами Unicode. Чтобы сохранить эти пробелы в исходном виде, вы можете либо заключить их в кавычки ( <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-теги с экранированными сущностями, которые затем восстанавливаются с помощью 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> . Обратите внимание, что открывающая скобка экранирована с помощью HTML-кода &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)
}

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

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

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

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

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