• Localization

    • 앱이 고객의 언어로 말할 수 있게 만드는 것

      • 단어 번역 수준이 아니라 그 뒤에 숨겨진 뉘앙스들까지도 함께 전달하는 과정
    • 유저는 OS 설정에서 자신이 선호하는 언어를 순서대로 지정할 수 있다.

      • 앱에 순서대로 해당 언어를 지원하는지를 요청하고, 지원 안하면 다음으로 넘어간다.
    • 언어와는 별개로 지역도 지정할 수 있다.

      • 단위 표현, 시간, 날짜 등에 영향을 미친다.
    • iOS 9부터는 숫자 시스템도 유저가 지정할 수 있다.

      • 대부분 아라비아 숫자를 쓰지만, 다른 숫자 시스템을 쓰는 언어의 경우는 이를 전환할 수 있다.
    • iOS 9에 새로 추가된 키보드

      • Hindi(Latin) a.k.a Hinglish
      • Hindi(Transliteration)
      • Punjabi
      • Telugu
      • Gujarati
    • 타이핑 예측 지원 추가

      • Korean
      • Mexican Spanish
      • Russian
      • Turkish
    • 특정 언어(ex. Telugu)는의 키보드가 입력하는 단위가 실제 보여지는 것과 차이가 날 수 있다.

      • 2개 이상의 Character가 합쳐져서 하나의 visible unit을 구성하는 경우
    • 유저가 보는 문자열을 번역하려면 어떻게 해야 할까?

      • 주요한 인터페이스는 .strings 파일이다.
    • .strings 파일 포맷

      • 주석은 C 스타일(/* */)

      • 개발용 키값은 왼쪽, 타겟 언어 값은 오른쪽

      • lproj 디렉토리에 저장된다.

        스크린샷 2022-11-23 오후 3.08.59.png

    • Xcode 6부터 localization을 일반적으로 쓰는 XLIFF 형태로 내보낼 수 있게 되었고, 이를 다시 .strings 파일로 바꿔서 import할 수도 있다.

    • 개발자가 코드레벨에서 쓰기 위해서는 NSLocalizedString을 써야 한다.

      • Objective-C에서는 매크로였지만, Swift에서는 일급 함수로 다뤄진다.

        • comment가 중요하기 때문에 non-optional로 뒀다.
        func NSLocalizedString(
            _ key: String,
            tableName: String? = nil,
            bundle: Bundle = Bundle.main,
            value: String = "",
            comment: String
        ) -> String
        
      • 포매팅이 필요한 문자열은 다음 메소드를 쓴다.

        static func localizedStringWithFormat(
            _ format: String,
            _ arguments: CVarArg...
        ) -> String
        
      • 조합해서 다음과 같이 많이 쓴다.

        String.localizedStringWithFormat(NSLocalizedString("%@ %d", comment: ...), ...)
        

        스크린샷 2022-11-23 오후 3.31.16.png

    • 이 때 주의할 점은 인자로 주어진 값이 항상 동일하지 않을 수 있다는 것이다.

      • 특정 언어에서는 이 순서가 뒤집할 수 있다.

        스크린샷 2022-11-23 오후 3.33.42.png

      • 이럴 때 인자의 순서를 별도로 지정해 줄 수 있다.

        스크린샷 2022-11-23 오후 3.34.30.png

      • 인자가 무조건 순서대로 들어올 것이라는 가정은 금지.

    • 이걸 알고나면 왠지 직접 접근하고 싶은 마음이 들수도 있다.

      let lang = NSLocale.perferredLanguages().firstObject!
      let lprojPath = lange.stringByAppendingPathExtension("lproj")
      let filePath = NSBundle.mainBundle().pathForResourece("stopSign", ofType: "png", inDirectory: lprojPath)
      
    • 하지만 이렇게 하면 유저가 원하는 언어가 없을 때 fallback을 하는 로직이 없어진다.

      • 내부적으로 스마트한 fallback 로직이 있다.

        • es-MX → es, en-IN → en-GB 등…
      • 그래서 다음과 같이 쓰는 것을 권장한다.

        • NSBundle의 API를 쓸 것
        NSBundle.mainBundle().imageForResource("stopSign")
        NSBundle.mainBundle().pathForSoundResource("greeting")
        NSBundle.mainBundle().URLForResource("help", withExtension: "pdf")
        
    • 근데 코드로만 할 수 있는 작업이 여전히 있지 않을까? 복수형 처리라던지.

      if numDays == 1 {
      	daysString = String.localizedStringWithFormat(
      										NSLocalizedString("%d day remaining",
      																			comment: "Single day remaining")
      										numDays)
      } else {
      	daysString = String.localizedStringWithFormat(
      										NSLocalizedString("%d days remaining",
      																			comment: "number of days remaining")
      										numDays)
      }
      
    • 근데 이것도 언어마다 달라진다.

      • 이런 상황에서 쓸 수 있는 것이 .stringdict다
    • .stringdict는 근본적으로 프로젝트 안에 있는 plist 파일이다.

      • localized 리소스기 때문에 lproj 디렉토리 안에 있게 된다.

      • key가 있고, 이 키를 숫자 인자와 조합해서 다른 값을 찾아올 수 있다.

        스크린샷 2022-11-24 오전 9.54.13.png

    • iOS 9부터 stringdict에 VariableWidth 기능이 추가 됐다.

      • 가용한 width를 기준으로 내용을 변경하는 것

        • 패드, 아이폰, 아이팟 터치 등을 한번에 대응하기 위해서.
      • 단위는 시스템 폰트의 대문자 M → 왜 이런 애매한 단위를…?

        스크린샷 2022-11-24 오전 10.06.48.png

      • NSLocalizedString을 써서 표준 UIKit control에 넣으면 이를 적용해준다.

      • macOS에서는 이를 수동으로 적용해야 한다.

        let formatted = NSLocalizedString("Welcome", comment: "Welcome the user")
        let widthFormattedString = formatted.variantFittingPresentationWidth(20)
        
  • Formatting

    • 숫자, 날짜, 시간, 이름등의 포매팅도 지역마다 다를 수 있다.

      let pi = String(format: "%.3f", M_PI) // 3.142
      // 독일에서는 저걸 3142로 읽는다. 숫자에서 점과 쉼표를 쓰는 방식이 반대기 때문.
      
    • 이를 위해서는 역시 localizedStringWithFormat을 써야 한다.

      let pi = String.localizedStringWithFormat("%.3f", M_PI)
      
    • 내부적으로 NSNumberFormatter를 쓰는데, 여기도 몇가지 변경 사항이 있다.

      • 스타일 추가
        • CurrencyISOCodeStyle
        • CurrencyPluralStyle
        • CurrencyAccountingStyle
        • OrdinaryStyle
    • 날짜도 지역마다 다르다.

      let date = String(format: "%d/%d/%d, %d:%d:%d", 6, 12, 2015, 9, 0, 0)
      // 6/12/2015, 9:00
      // 미국에서는 6월 12일인데, 이탈리아에서는 12월 6일이 된다. 
      
    • NSDateFormatter를 써서 문제를 해결할 수 있지만, 잘못된 방법으로 사용할 수 있다.

      let df = NSDateFormatter()
      df.dateFormat = "MM/dd/yyyy, h:mm a"
      print(df.stringFromDate(NSDate())) // 06/12/2015, 9:00 AM
      
    • 제대로 쓰기 위해서는 dateStyle과 timeStyle을 사용해야 한다.

      let df = NSDateFormatter()
      df.dateStyle = .ShortStyle
      df.timeStyle = .ShortStyle
      print(df.stringFromDate(NSDate()))
      // 06/12/2015, 9:00 AM
      // 이탈리아 지역에서는 12/06/2015, 9:00 AM
      
    • 근데 이것도 커스텀으로 하고 싶은 경우가 있을 수 있다. 그래서 새로운 API를 추가했다.

    • 단위

    • iOS 9부터 nameFormatter가 추가되었다.

  • Handling Text(Characters, Case Changes, Searching, Transforms)

  • Layout