문자열 리소스

문자열 리소스를 사용하여 애플리케이션의 텍스트 문자열에 최적의 텍스트 스타일과 서식을 지정할 수 있습니다. 애플리케이션에 문자열을 제공할 수 있는 리소스 유형으로는 세 가지가 있습니다.

String
단일 문자열을 제공하는 XML 리소스입니다.
문자열 배열
문자열로 구성된 배열을 제공하는 XML 리소스입니다.
수량 문자열 (복수형)
복수형 표시를 위해 여러 문자열을 포함하는 XML 리소스입니다.

모든 문자열은 몇 가지 스타일 지정 마크업 및 서식 지정 인수를 적용할 수 있습니다. 문자열의 스타일 및 서식을 지정하는 방법에 관한 자세한 내용은 서식 지정 및 스타일 지정 관련 섹션을 참고하세요.

문자열

애플리케이션 코드 (예: 컴포저블 함수) 또는 다른 리소스 파일에서 참조할 수 있는 단일 문자열입니다.

파일 위치:
res/values/filename.xml
파일 이름은 임의로 지정됩니다. <string> 요소의 name이 리소스 ID로 사용됩니다.
컴파일된 리소스 데이터 유형:
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
문자열. 문자열의 이름입니다. 이 이름이 리소스 ID로 사용됩니다.
예:
res/values/strings.xml에 저장된 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)를 사용하세요.

AndroidManifest.xml과 같은 다른 XML 파일에서 문자열 리소스를 참조할 수도 있습니다.
<activity
    android:name=".MainActivity"
    android:label="@string/hello" />

문자열 배열

애플리케이션에서 참조될 수 있는 문자열로 구성된 배열입니다.

파일 위치:
res/values/filename.xml
파일 이름은 임의로 지정됩니다. <string-array> 요소의 name이 리소스 ID로 사용됩니다.
컴파일된 리소스 데이터 유형:
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
문자열. 배열의 이름. 이 이름이 배열을 참조하기 위한 리소스 ID로 사용됩니다.
<item>
스타일 지정 태그를 포함할 수 있는 문자열입니다. 이 값은 다른 문자열 리소스에 대한 참조일 수 있습니다. <string-array> 요소의 하위 요소여야 합니다. 아포스트로피와 따옴표는 이스케이프 처리해야 합니다. 문자열에 적절한 스타일과 서식을 지정하는 방법에 관한 자세한 내용은 아래에 나와 있는 서식 지정 및 스타일 지정을 참고하세요.

속성 없음

예:
res/values/strings.xml에 저장된 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의 경우 '1 book'으로 표기하지만 다른 수량의 경우 'n books'로 표기합니다. 단수형과 복수형 간의 이러한 구별은 매우 일반적인 것이며, 다른 언어에서는 더 미세하게 구별하기도 합니다. Android에서 지원하는 전체 집합은 zero, one, two, few, many, other입니다.

지정된 언어 및 수량에 사용할 사례를 결정하기 위한 규칙은 매우 복잡할 수 있으므로, Android는 적합한 리소스를 선택하는 pluralStringResource()과 같은 메서드를 제공합니다.

예전부터 '수량 문자열' (그리고 API에서도 여전히 이렇게 부름)이라고 불렀지만 수량 문자열은 오직 복수형에만 사용되어야 합니다. 예를 들어 읽지 않은 메시지가 있는 경우 수량 문자열을 사용하여 Gmail의 '받은편지함' 대 '받은편지함 (12)'과 같은 형식을 구현하는 것은 좋지 않을 수 있습니다. if 문 대신 수량 문자열을 사용하는 것이 편해 보일 수 있지만, 일부 언어 (예: 중국어)의 경우 이러한 문법적 구분을 전혀 하지 않으므로 항상 other 문자열을 사용해야 합니다.

어느 문자열을 사용할지 선택할 때는 전적으로 문법적 필요성만을 근거로 해야 합니다. 영어에서는 수량이 0인 경우에도 zero 문자열은 무시됩니다. 0이 2, 또는 1을 제외한 다른 어떤 숫자와도 문법적으로 다르지 않기 때문입니다('zero books', 'one book', 'two books' 등). 반대로 한국어의 경우 other 문자열 사용합니다.

two가 수량 2에만 적용될 수 있는 듯이 들리는 것에 현혹되지 마세요. 2, 12, 102 (기타 등등)가 모두 서로 같게 취급되지만 다른 수량과 다르게 취급되도록 요구하는 언어가 있을 수 있습니다. 번역자에게 문의하여 언어에서 실제로 어떠한 구분이 필요한지 파악해야 합니다.

메시지에 수량 번호가 포함되어 있지 않다면 복수형에 좋은 후보가 아닐 수 있습니다. 예를 들어 리투아니아어에서는 1과 101 모두에 단수형이 사용되므로 '1 book'은 '1 knyga'로 번역되고 '101 Books'는 '101 knyga'로 번역됩니다. 반면 'a book'은 'knyga'이고 'many books'는 'daug knygų'입니다. 영어 복수 메시지에 실제 숫자 없이 'a book' (단수)과 'many books' (복수)가 포함된 경우 'knyga' (책 한 권)/'daug knygų' (책 여러 권)로 번역될 수 있지만, 리투아니아어 규칙에 따라 숫자가 101인 경우 'knyga' (책 한 권)가 표시됩니다.

'Books: 1'과 같이 수량 중립적인 표현을 사용하면 수량 문자열을 방지할 수 있는 경우가 많습니다. 이것이 애플리케이션에 적합한 스타일이라면 개발자와 번역자 모두에게 편리합니다.

API 24 이상에서는 훨씬 더 강력한 ICU MessageFormat 클래스를 대신 사용할 수 있습니다.

파일 위치:
res/values/filename.xml
파일 이름은 임의로 지정됩니다. <plurals> 요소의 name이 리소스 ID로 사용됩니다.
리소스 참조:
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
문자열. 문자열 쌍의 이름입니다. 이 이름이 리소스 ID로 사용됩니다.
<item>
복수형 또는 단수형 문자열입니다. 이 값은 다른 문자열 리소스에 대한 참조일 수 있습니다. <plurals> 요소의 하위 요소여야 합니다. 아포스트로피와 따옴표는 이스케이프 처리해야 합니다. 문자열에 적절한 스타일과 서식을 지정하는 방법에 관한 자세한 내용은 아래에 나와 있는 서식 지정 및 스타일 지정을 참고하세요.

속성:

quantity
키워드. 이 문자열이 사용되는 시기를 나타내는 값입니다. 유효한 값(괄호 안에 일부 예가 포함되어 있음):
설명
zero언어가 숫자 0에 대한 특수한 처리를 요구하는 경우(예: 아랍어)
one언어가 1과 같은 숫자에 대한 특수한 처리를 요구하는 경우(예: 영어 및 기타 대부분 언어의 경우 숫자 1. 러시아어의 경우 1로 끝나지만 11로 끝나지 않는 숫자(이 클래스에 나와 있음))
two언어가 2와 같은 숫자에 대한 특수한 처리를 요구하는 경우(예: 웨일스어의 경우 2 또는 슬로베니아어의 경우 102)
few언어가 '작은' 숫자에 대한 특수한 처리를 요구하는 경우(예: 체코어의 경우 2, 3, 4 또는 폴란드어의 경우 2, 3 또는 4로 끝나지만 12, 13 또는 14로 끝나지 않는 숫자)
many언어가 '큰' 숫자에 대한 특수한 처리를 요구하는 경우(예: 몰타어로 11~99로 끝나는 숫자)
other언어가 지정된 수량에 대한 특수한 처리를 요구하지 않는 경우(예: 중국어의 경우 모든 숫자 또는 영어의 경우 42)
예:

res/values/strings.xml에 저장된 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>

res/values-pl/strings.xml에 저장된 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").
큰따옴표(") \"

문자열을 작은따옴표로 묶는 것은 작동하지 않습니다.

리소스 파일이 XML로 파싱된 후 공백 축소 및 Android 이스케이프가 발생합니다. 즉, <string> &#32; &#8200; &#8195;</string>(공백, 구두점 공백, 유니코드 Em 공백)은 파일이 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 태그를 쓰는 것입니다. 그러면 서식 지정이 실행된 후 이 항목이 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 항목의 서식을 지정하므로 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 텍스트 객체입니다. buildAnnotatedStringwithStyle을 사용하여 스타일이 지정된 텍스트를 프로그래매틱 방식으로 빌드합니다.

이 애플리케이션 코드는 스타일이 혼합된 단일 텍스트 요소를 만듭니다.

@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> 접근 방식을 참고하세요.

주석으로 번역된 문자열 스타일 지정

맞춤 스타일 지정 번역이 필요한 문자열의 경우 각 언어의 strings.xml<annotation> 태그를 정의합니다. 번역기는 문장에서 주석이 어디에 위치하든 주석을 유지합니다. 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)
}

XML의 <annotation> 태그는 변경되지 않습니다. 검색 코드만 다릅니다. 번역자는 여전히 각 언어에서 올바른 단어를 래핑하기 위해 태그를 이동합니다.

추가 리소스

문자열 리소스에 관한 자세한 내용은 다음 추가 리소스를 참고하세요.

문서

콘텐츠 보기