I18n ==== GNU gettext 가 사용됩니다. gettext는 다른 국제화 방법과는 다르게 복수형을 지원합니다. .. image:: http://poedit.net/images/home_image2-big.png 소스코드에 국제화 메세지 작성 --------------------- ``xitrum.Action`` 은 ``xitrum.I18n`` 확장했으며 다음의 메소드가 있습니다: :: t("Message") tc("Context", "Message") t("Hello %s").format("World") // 1$ and 2$ are placeholders t("%1$s says hello to %2$s, then %2$s says hello back to %1$s").format("Bill", "Hillary") // {0} and {1} are placeholders java.text.MessageFormat.format(t("{0} says hello to {1}, then {1} says hello back to {0}"), "Bill", "Hillary") t("%,.3f").format(1234.5678) // => 1,234.568 t("%,.3f").formatLocal(java.util.Locale.FRENCH, 1234.5678) // => 1 234,568 :: // Above, you explicitly specify locale. // If you want to implicitly use locale of the current action: // when English => 1,234.568, when French => 1 234,568 t("%,.3f", 1234.5678) action 안에서나 혹은 액션에서 호출할 수 있습니다. model과 같은 곳에서의 사용은 현재의 액션에서 ``t`` 나 ``tc`` 를 호출하여 넘겨줘야 합니다: :: // In an action respondText(MyModel.hello(this)) // In the model import xitrum.I18n object MyModel { def hello(i18n: I18n) = i18n.t("Hello World") } pot에 메세지 추출하기 ---------------- 프로젝트 루트에 빈 i18n.pot 파일을 생성하여 전체 프로젝트를 다시 컴파일 하면 됩니다. :: sbt/sbt clean rm i18n.pot touch i18n.pot sbt/sbt compile ``sbt/sbt clean`` 는 모든 .class파일을 삭제하고 전체를 다시 컴파일 합니다. ``sbt/sbt clean`` 명령은 SBT가 모든 :doc:`의존된 ` 라이브러리를 다운받기 때문에 ``find target -name *.class -delete`` 명령이 조금 더 빠르지만 ``target`` 내부의 .class 파일을 삭제하는것은 동일합니다. 재컴파일 후에 i18n.pot는 소스코드로 부터 추출된 gettext 메세지를 채웁니다. 마법같은 이 동작은 `Scala compiler plugin technique `_ 에 기술되어 있습니다. 한 가지 이 메소드의 주의점은 gettext는 Scala 소스 코드로부터 메세지를 추출합니다. 만약 자바 파일을 사용한다면 다음과 같이 ``xgettext`` 커맨드 라인을 사용하여야 합니다. :: xgettext -kt -ktc:1c,2 -ktn:1,2 -ktcn:1c,2,3 -o i18n_java.pot --from-code=UTF-8 $(find src/main/java -name "*.java") 그리고 나서 수동으로 i18n_java.pot 파일과 i18n.pot파일을 병합해야 합니다. po 파일 저장위치 ------------ i18n.pot은 임시 파일입니다. 파일들을 .po 로 복사하여 번역해야 합니다. Xitrum은 클래스 패스상의 ``i18n`` 디렉토리를 모니터링합니다. 만약 런타임시 디렉토리 상의 .po 파일이 변경되거나 추가된다면 Xitrum은 자동으로 .po 파일들을 다시 로드합니다. :: src main scala view resources i18n ja.po vi.po ... po 파일을 수정하기 위해서는 `Poedit `_ 와 같은 툴을 사용하면 됩니다. 툴을 사용하여 새로운 pot 파일을 기존의 po 파일에 추가 할수 있습니다. po 파일들은 여러 JAR파일들에 패키징할 수 있고 Xitrum은 실행시에 자동으로 병합합니다. :: mylib.jar i18n ja.po vi.po ... another.jar i18n ja.po vi.po ... 언어설정 ------ * 브라우저의 요청 헤더의 ``Accept-Language`` 에서 언어 셋을 가져오려면 ``browserLanguages`` 를 호출하면 됩니다. 결과는 브라우저의 우선순위에 따라 정렬됩니다. * 기본 언어는 "en" 입니다. 현재의 언어로 세팅하기 위해서는, 예를 들어, 일어일 경우 ``language = "ja"`` 로 하면 됩니다. * 가장 적절한 언어를 리소스에서 찾아 자동세팅을 위해서는 ``autosetLanguage(availableLanguages)`` 을 호출하면 됩니다. ``availableLanguages`` 는 ``resources/i18n`` 디렉토리와 JAR파일들의 가능한 언어들의 리스트를 가지고 있습니다. 만약 적절한 언어가 없을 경우에는 기본언어인 "en"으로 설정합니다. * 현재의 언어셋을 확인하려면 ``language`` 를 사용하면 됩니다. 일반적으로 액션의 before 필터에서 언어를 세팅합니다: :: beforeFilter { val lango: Option[String] = yourMethodToGetUserPreferenceLanguageInSession() lango match { case None => autosetLanguage(Locale.forLanguageTag("ja"), Locale.forLanguageTag("vi")) case Some(lang) => language = lang } true } 검증 메세지 -------- jQuery Validation 플러그인은 `i18n error messages `_ 제공합니다. Xitrum은 자동으로 현재의 언어에 상응하는 메세지를 가져옵니다. 서버의 기본 검증인 ``xitrum.validator`` 패키지, 또한 Xitrum은 제공하고 있습니다. 복수형 ----- :: tn("Message", "Plural form", n) tcn("Context", "Message", "Plural form", n) Xitrum은 다음에 정의된 복수형만을 사용합니다: * `What are plural forms `_ * `Translating plural forms `_ 복수형은 다음 중 하나를 사용해야 합니다: :: nplurals=1; plural=0 nplurals=2; plural=n != 1 nplurals=2; plural=n>1 nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : 2 nplurals=3; plural=n==1 ? 0 : n==2 ? 1 : 2 nplurals=3; plural=n==1 ? 0 : (n==0 || (n%100 > 0 && n%100 < 20)) ? 1 : 2 nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && (n%100<10 || n%100>=20) ? 1 : 2 nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2 nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2 nplurals=3; plural=n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2 nplurals=4; plural=n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3 날짜와 시간 포멧 ------------ 만약 Scalate 템플릿 엔진을 사용한다면 날짜와 시간 포멧은 현재 액션의 언어 포멧을 따르게 됩니다. 다른 포멧을 사용하고자 하는 경우: :: import java.text.{DateFormat, NumberFormat} val myDateFormat = ... val myNumberFormat = ... val options = Map("date" -> myDateFormat, "number" -> myNumberFormat) respondView(options)