[Android] toLowerCase()

회사에서 개발하여 서비스 중인 앱에 이상한 버그 하나가 터키로 부터 report 됐다.

 

버그 리포트에 명시된 증상은 아래와 같다.

정상적으로 앱을 잘 쓰고 있었는데, 언어 설정을 Turkey로 변경한 뒤 앱을 재실행하니 Crash가 발생한다.

* 언어를 Turkey 외의 언어로 다시 변경하면 원래대로 정상동작한다.

 

이 알 수 없는 버그의 원인은 아래와 같다.

1. 앱이 실행되는 시점에 XML파일을 Parsing한다.

2. Parser는 내부적으로  toLowerCase() 함수를 사용한다.

3. 언어 설정이 Turkish일 때 I는 i가 아닌 ı (dotless i)로 치환된다.

http://developer.android.com/reference/java/util/Locale.html#default_locale

4. Parsing이 정상적으로 수행되지 않은 상태로 종료된다.

 

왜 이런 일이 발생하는 걸까 확인해보기 위해 toLowerCase() 함수의 설명을 Android Developer site에서 찾아보니 아래와 같은 설명이 첨부되어 있다.

Most case mappings are unaffected by the language of a Locale. Exceptions include dotted and dotless I in Azeri and Turkish locales, and dotted and dotless I and J in Lithuanian locales. On the other hand, it isn’t necessary to provide a Greek locale to get correct case mapping of Greek characters: any locale will do.

See http://www.unicode.org/Public/UNIDATA/SpecialCasing.txt for full details of context- and language-specific special cases.

요약하면 대부분의 경우 toLowerCase() 함수는 Locale에 영향을 받지 않는데, 영향을 받는 2가지 예외가 있다.

1. Azeri와 Turkish의 경우 I가 dotless(ı)로 치환되는 경우.

2. Lithuanian의 경우 I와 J가 dotless(ı, ȷ)로 치환되는 경우.

 

 

추가로 Java doc에서 찾아본 toLowerCase()의 설명은 아래와 같다.

Converts all of the characters in this String to lower case using the rules of the default locale. This is equivalent to calling toLowerCase(Locale.getDefault()).

Note: This method is locale sensitive, and may produce unexpected results if used for strings that are intended to be interpreted locale independently. Examples are programming language identifiers, protocol keys, and HTML tags. For instance,"TITLE".toLowerCase() in a Turkish locale returns "t\u0131tle", where ‘\u0131′ is the LATIN SMALL LETTER DOTLESS I character. To obtain correct results for locale insensitive strings, use toLowerCase(Locale.ENGLISH).

toLowerCase()는 toLowerCase(Locale.getDefault()) 함수를 호출하는 것과 같은데 toLowerCase() 함수는 Locale sensitive하기 때문에 Locale 설정에 따라 예상하지 못한 결과가 나올 수 있다.

toLowerCase() 함수가 Locale sensitive하다는 부분에 ★ 10개 주고 싶다.

 

* Turkish와 Azeri의 경우 I를 dotless i로 치환하는 이유는 아래 표를 보면 알 수 있는데 두 언어에는 대문자 I가 dot above, dotlesss로 나뉘는데 각각의 대문자 I가 서로 다른 소문자 i로 변경되어야 하기 때문에 이런 결과가 발생한다. (http://en.wikipedia.org/wiki/Dotted_and_dotless_I)

Language Code Upper Case Lower Case Description
tr (Turkish) \u0130 \u0069 capital letter I with dot above -> small letter i
tr (Turkish) \u0049 \u0131 capital letter I -> small letter dotless i