Recursos de cadenas

Un recurso de cadenas proporciona cadenas de texto para tu aplicación con formato y estilo de texto opcionales. Existen tres tipos de recursos que pueden proporcionar strings para tu aplicación:

String
Recurso XML que proporciona una sola cadena.
Array de cadenas
Recurso XML que proporciona un array de cadenas.
Strings de cantidad (plurales)
Recurso XML que conlleva diferentes strings para brindar pluralización.

Todas las cadenas pueden aplicar algunos argumentos de formato y marcado de estilo. Para obtener información sobre las cadenas de formato y estilo, consulta la sección sobre Formato y estilo.

String

Una sola cadena a la que se puede hacer referencia desde el código de la aplicación (por ejemplo, una función de componibilidad) o desde otros archivos de recursos.

ubicación del archivo:
res/values/filename.xml
El nombre del archivo es arbitrario. El name del elemento de <string> se usa como ID del recurso.
tipo de datos de recursos compilados:
Puntero de recursos a un String.
referencia del recurso:
En Kotlin: R.string.string_name
En XML: @string/string_name
sintaxis:
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string
        name="string_name"
        >text_string</string>
</resources>
elementos:
<resources>
Obligatorio. Este debe ser el nodo raíz.

Sin atributos.

<string>
Cadena que puede incluir etiquetas de estilo. Ten en cuenta que se deben escapar los apóstrofos y las comillas. Para obtener más información sobre la manera de dar estilo y formato adecuadamente a tus strings, consulta Formato y estilo a continuación.

atributos:

name
String. Nombre para la cadena. El nombre se usa como ID del recurso.
ejemplo:
Archivo en formato XML guardado en res/values/strings.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="hello">Hello!</string>
</resources>

Este código de la aplicación recupera una cadena desde el interior de un elemento componible con stringResource():

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

Nota: Para recuperar una cadena fuera de una función de componibilidad, usa context.getString(R.string.hello).

También puedes hacer referencia a recursos de cadenas desde otros archivos XML, como tu AndroidManifest.xml:
<activity
    android:name=".MainActivity"
    android:label="@string/hello" />

Matriz de string

Array de cadenas al cual se puede hacer referencia desde la aplicación.

ubicación del archivo:
res/values/filename.xml
El nombre del archivo es arbitrario. El name del elemento de <string-array> se usa como ID del recurso.
tipo de datos de recursos compilados:
Puntero de recurso para un elemento de array de Strings.
referencia del recurso:
En Kotlin: R.array.string_array_name
En XML: @[package:]array/string_array_name
sintaxis:
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string-array
        name="string_array_name">
        <item
            >text_string</item>
    </string-array>
</resources>
elementos:
<resources>
Obligatorio. Este debe ser el nodo raíz.

Sin atributos.

<string-array>
Define un array de strings. Contiene uno o más elementos <item>.

atributos:

name
String. Es un nombre para el array. El nombre se usa como ID del recurso para hacer referencia al array.
<item>
Cadena que puede incluir etiquetas de estilo. El valor puede ser una referencia a otro recurso de strings. Tiene que ser un elemento secundario de un elemento <string-array>. Ten en cuenta que se deben escapar los apóstrofos y las comillas. Consulta Formato y estilo a continuación para obtener información sobre cómo dar formato y estilo adecuadamente a tus cadenas.

Sin atributos.

ejemplo:
Archivo en formato XML guardado en 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>

Este código de la aplicación recupera un array de cadenas desde el interior de un elemento componible con stringArrayResource():

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

Nota: Para recuperar un array de cadenas fuera de una función de componibilidad, usa context.resources.getStringArray(R.array.planets_array).

Strings de cantidad (plurales)

Los diferentes idiomas tienen distintas reglas de concordancia gramatical con la cantidad. En inglés, por ejemplo, la cantidad 1 es un caso especial. Escribimos "1 libro", pero, para las demás cantidades, se debe escribir "n libros". Esta distinción entre singular y plural es muy común; sin embargo, en otros idiomas, se hacen distinciones más detalladas. El conjunto completo compatible con Android es zero, one, two, few, many y other.

Las reglas para decidir el caso que se usará para un idioma y una cantidad determinados pueden ser muy complejas, por lo cual Android te proporciona métodos como pluralStringResource() para seleccionar el recurso adecuado para ti.

Si bien históricamente se llamaron "cadenas de cantidad" (y aún se llaman así en la API), las cadenas de cantidad solo deben usarse para los plurales. Sería un error usar cadenas de cantidad para implementar algo como "Recibidos" de Gmail en comparación con "Recibidos (12)" cuando existen mensajes no leídos, por ejemplo. Podría parecer conveniente usar cadenas de cantidad en lugar de una instrucción if, pero es importante tener en cuenta que, en algunos idiomas (como el chino), no se hacen estas distinciones gramaticales en absoluto, por lo que siempre obtendrás la cadena other.

La selección de la cadena que se debe usar se realiza únicamente según la necesidad gramatical. En inglés, una cadena para zero se ignora incluso si la cantidad es 0, ya que 0 no es gramaticalmente diferente de 2, o cualquier otro número, excepto 1 ("zero books", "one book", "two books", y así sucesivamente). En cambio, en coreano, solo se usa la cadena other.

No te confundas por el hecho de que, por ejemplo, two suena como si solo pudiera aplicarse a la cantidad 2: un idioma puede requerir que 2, 12, 102 (y así sucesivamente) se traten de manera similar, pero de forma diferente de otras cantidades. Confía en tu traductor para conocer las distinciones de su idioma.

Si tu mensaje no contiene el número de cantidad, es probable que no sea un buen candidato para un plural. Por ejemplo, en lituano, la forma singular se usa para los números 1 y 101, por lo que "1 libro" se traduce como "1 knyga" y "101 libros" como "101 knyga". Mientras que "un libro" es "knyga", "muchos libros" es "daug knygų". Si un mensaje en inglés en plural contiene "un libro" (singular) y "muchos libros" (plural) sin el número real, se puede traducir como "knyga" (un libro) o "daug knygų" (muchos libros), pero con las reglas del lituano, se mostrará "knyga" (un solo libro) cuando el número sea 101.

En ocasiones, es posible evitar las cadenas de cantidad usando fórmulas de cantidad neutra, por ejemplo, "Libros: 1". Esto hará más fáciles las cosas para ti y tus traductores si se trata de un estilo aceptable para tu aplicación.

En el nivel de API 24 y versiones posteriores, puedes usar la clase MessageFormat de ICU mucho más potente en su lugar.

ubicación del archivo:
res/values/filename.xml
El nombre del archivo es arbitrario. El name del elemento de <plurals> se usa como ID del recurso.
referencia del recurso:
En Kotlin: R.plurals.plural_name
sintaxis:
<?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>
elementos:
<resources>
Obligatorio. Este debe ser el nodo raíz.

Sin atributos.

<plurals>
Una colección de cadenas, de las cuales se proporciona una cadena según la cantidad de algún elemento. Contiene uno o más elementos <item>.

atributos:

name
String. Nombre para el par de cadenas. El nombre se usa como ID del recurso.
<item>
Una cadena singular o plural. El valor puede ser una referencia a otro recurso de strings. Tiene que ser un elemento secundario de un elemento <plurals>. Ten en cuenta que se deben escapar los apóstrofos y las comillas. Consulta Formato y estilo a continuación para obtener información sobre cómo dar formato y estilo adecuadamente a tus cadenas.

atributos:

quantity
Palabra clave. Valor que indica cuándo debe usarse esta cadena. Valores válidos, con ejemplos no exhaustivos entre paréntesis:
ValorDescripción
zeroCuando el idioma requiere tratamiento especial del número 0 (como en árabe).
oneCuando el idioma requiere tratamiento especial de números como el uno (como el número 1 en inglés y la mayoría de los demás idiomas; en ruso, se encuentran dentro de esta clase todos los números que finalizan en 1, pero no en 11).
twoCuando el idioma requiere tratamiento especial de números como el dos (como el 2 en galés, o el 102 en esloveno).
fewCuando el idioma requiere tratamiento especial de números "pequeños" (como el 2, el 3 y el 4 en checo, o los números que finalizan en 2, 3 o 4, excepto el 12, el 13 o el 14 en polaco).
manyCuando el idioma requiere tratamiento especial de números "grandes" (como los números que finalizan en 11-99 en maltés).
otherCuando el idioma no requiere tratamiento especial de la cantidad especificada (como todos los números en chino o el 42 en inglés).
ejemplo:

Archivo en formato XML guardado en 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>

Archivo en formato XML guardado en 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>

Este código de la aplicación recupera una cadena plural desde un elemento componible con pluralStringResource():

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

Cuando se usa la función pluralStringResource(), debes pasar count dos veces si tu cadena incluye formato de cadena con un número. Por ejemplo, para la string %d songs found, el primer parámetro de count selecciona el plural adecuado, y el segundo parámetro de count se inserta en el marcador de posición %d. Si tus strings de plural no incluyen formato de string, no es necesario que pases el tercer parámetro a pluralStringResource.

Nota: Para recuperar una cadena plural fuera de una función de componibilidad, usa context.resources.getQuantityString(R.plurals.numberOfSongsAvailable, count, count).

Formato y estilo

A continuación, se mencionan algunos puntos importantes que debes conocer sobre cómo dar formato y estilo adecuadamente a tus recursos de cadenas.

Caracteres especiales

Cuando una cadena contiene caracteres que tienen un uso especial en XML, debes escapar los caracteres de acuerdo con las reglas de escape estándar de XML/HTML. Si necesitas escapar un carácter que tiene un significado especial en Android, debes usar antes una barra inversa.

De forma predeterminada, Android contraerá secuencias de caracteres de espacios en blanco en un solo espacio. Para evitar esto, encierra la parte relevante de la cadena entre comillas dobles. En este caso, todos los caracteres de espacios en blanco (incluidas las nuevas líneas) se conservarán dentro de la región entrecomillada. Las comillas dobles también te permitirán usar comillas simples regulares sin escapar.

Carácter Formas de escape
@ \@
? \?
Nueva línea \n
Tab \t
carácter Unicode U+XXXX \uXXXX
Comilla simple (')

Cualquiera de los siguientes:

  • \'
  • Encierra toda la string entre comillas dobles ("This'll work", por ejemplo)
Comillas dobles (") \"

Ten en cuenta que encerrar la string entre comillas simples no funciona.

La contracción del espacio en blanco y el escape de Android ocurren después de que se analiza tu archivo de recursos como XML. Esto significa que <string> &#32; &#8200; &#8195;</string> (espacio, espacio de puntuación, espacio largo de Unicode) se contraen a un solo espacio (" "), ya que son todos espacios de Unicode después de que se analiza el archivo como XML. Para conservar esos espacios como están, puedes entrecomillarlos (<string>" &#32; &#8200; &#8195;"</string>) o usar el escape de Android (<string> \u0032 \u8200 \u8195</string>).

Cómo dar formato a las strings

Si necesitas dar formato a tus cadenas, puedes hacerlo disponiendo tus argumentos de formato en el recurso de cadenas, tal como se demuestra en el siguiente recurso de ejemplo.

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

Este código de la aplicación formatea la cadena desde un elemento componible pasando argumentos directamente a stringResource():

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

Cómo dar estilo con el lenguaje de marcado HTML

Puedes agregar estilo a tus strings con el lenguaje de marcado HTML. Por ejemplo:

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

Se admiten los siguientes elementos HTML:

  • Negrita: <b>
  • Cursiva: <i>, <cite>, <dfn>, <em>
  • Texto 25% más grande: <big>
  • Texto 20% más pequeño: <small>
  • Configurar propiedades de la fuente: <font face="font_family" color="hex_color">. Algunas familias de fuentes posibles son monospace, serif y sans_serif.
  • Configurar una familia de fuentes monoespacio: <tt>
  • Tachado: <s>, <strike>, <del>
  • Subrayado: <u>
  • Superíndice: <sup>
  • Subíndice: <sub>
  • Viñetas: <ul> y <li>
  • Saltos de línea: <br>
  • División: <div>
  • Estilo de CSS: <span style="color|background_color|text-decoration">
  • Párrafos: <p dir="rtl | ltr" style="…">

En algunos casos, es posible que desees crear un recurso de texto con estilo que también se use como una cadena de formato. Generalmente, esto no funciona porque los métodos de formato, como stringResource(), quitan toda la información de estilo de la cadena. La solución temporal para esto es escribir las etiquetas HTML con entidades escapadas, que se recuperan después con AnnotatedString.fromHtml() una vez que se lleva a cabo el formato. Por ejemplo:

  1. Almacena tu recurso de texto con estilo como una string escapada con HTML:
    <resources>
      <string name="welcome_messages">Hello, %1$s! You have &lt;b>%2$d new messages&lt;/b>.</string>
    </resources>

    En esta string con formato, se agrega un elemento <b>. Ten en cuenta que el corchete de apertura está con escape HTML mediante la anotación &lt;.

  2. Luego, da formato a la cadena como siempre, pero también llama a AnnotatedString.fromHtml() para convertir el texto HTML en una cadena de Compose con estilo.

Debido a que fromHtml() da formato a todas las entidades HTML, asegúrate de escapar todos los caracteres HTML posibles en las cadenas que usas con el texto con formato, mediante 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)
    )
}

Cómo dar estilo con AnnotatedString

Un AnnotatedString es un objeto de texto de Compose al que puedes dar estilo con propiedades como el color y el grosor de la fuente. Crea texto con estilo de forma programática con buildAnnotatedString y withStyle.

Este código de la aplicación crea un solo elemento de texto con estilos mixtos:

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

Para aplicar color, tamaño de fuente y decoración de texto, usa SpanStyle. Para aplicar un diseño a nivel del párrafo (como la alineación o la altura de la línea), usa 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)
}

Compilar el AnnotatedString directamente es el enfoque recomendado para las apps en un solo idioma o el texto estático en Compose. Sin embargo, para el texto con estilo que requiere localización, consulta el enfoque de <annotation> en XML que se detalla en la siguiente sección.

Cómo dar estilo a cadenas traducidas con anotaciones

Para las cadenas que necesitan un diseño y una traducción personalizados, define la etiqueta <annotation> en el strings.xml de cada configuración regional. Los traductores conservan la anotación, independientemente de dónde se ubique en la oración. Lee la cadena con context.resources.getText(), recorre sus intervalos de Annotation y convierte el resultado en un 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)
}

La etiqueta <annotation> en tu archivo XML no se modificará. Solo difiere el código de recuperación. Los traductores aún mueven la etiqueta para envolver la palabra correcta en cada idioma.

Recursos adicionales

Para obtener más información sobre los recursos de cadena, consulta los siguientes recursos adicionales:

Documentación

Mira contenido