Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Swift Scripting Redux: Localization

Swift Scripting Redux: Localization

Talk given 14 June 2016 at AltConf in San Francisco

The first ever conference talk I gave was about [scripting in Swift](https://realm.io/news/swift-scripting/) in early 2015, pretty soon after Swift was released in mid 2014. Since then, I’ve been using Swift for even more scripting and automation tasks. Most recently, we’ve been working on internationalization and localization for the Workflow app. In this talk we’ll go over the steps that went into this localization process, how Swift makes things much more fun and easier, and how to get up and running with Swift scripting so you can start writing even more Swift!

D3c5f7382824133c2718c8effb5894fb?s=128

Ayaka Nonaka

June 14, 2016
Tweet

Transcript

  1. SWIFT Scripting LOCALIZATION AltConf 2016

  2. Hi! I’m Ayaka. @AYANONAGON

  3. WORKFLOW

  4. GERMANY CHINA JAPAN

  5. ENGLISH = > 300 MILLION NATIVE SPEAKERS

  6. SPANISH = > 400 MILLION NATIVE SPEAKERS

  7. CHINESE = > 1.3 BILLION NATIVE SPEAKERS

  8. MORE POTENTIAL USERS !

  9. MORE USERS !

  10. MORE PROFIT !

  11. MORE MONEY TO PUT BACK INTO YOUR PRODUCT !"

  12. HAPPY LONG-TERM USERS !

  13. NSLocalizedString

  14. MYCLASS.SWIFT let okButtonTitle = NSLocalizedString("OK", comment: "Alert button confirmation title")

    let cancelButtonTitle = NSLocalizedstring("Cancel", comment: "Alert button cancel title")
  15. $ genstrings *.swift

  16. BASE.LPROJ/LOCALIZABLE.STRINGS /* Alert button confirmation title */ "OK" = "OK";

    /* Alert button cancel title */ "Cancel" = "Cancel";
  17. NL.LPROJ/LOCALIZABLE.STRINGS /* Alert button confirmation title */ "OK" = "OK";

    /* Alert button cancel title */ "Cancel" = "Annuleer";
  18. JP.LPROJ/LOCALIZABLE.STRINGS /* Alert button confirmation title */ "OK" = "OK";

    /* Alert button cancel title */ "Cancel" = "Ωϟϯηϧ";
  19. “Run”

  20. “Run a Workflow”

  21. “Run a marathon”

  22. “Run a Workflow” “Run a marathon”

  23. ʮWorkflowΛ࣮ߦ͢Δʯ ʮϚϥιϯΛ૸Δʯ

  24. ࣮ߦ VS ૸Δ

  25. let runWorkflowTitle = NSLocalizedString("Run", comment: "Run workflow title") /* Run

    workflow title */ "Run" = "Run";
  26. let goOnARunTitle = NSLocalizedString("Run", comment: "Go on a run title")

    /* Go on a run title */ "Run" = "Run";
  27. let runWorkflowTitle = NSLocalizedString("Run", comment: "Run workflow title") let goOnARunTitle

    = NSLocalizedString("Run", comment: "Go on a run title") /* Run workflow title */ "Run" = "Run"; /* Go on a run title */ "Run" = "Run";
  28. /* Run workflow title */ "run-workflow.button.title" = "Run"; /* Go

    on a run title */ "go-on-a-run.button.title" = "Run";
  29. /* Run workflow title */ "RUN_WORKFLOW_BUTTON_TITLE" = "Run"; /* Go

    on a run title */ "GO_ON_A_RUN_BUTTON_TITLE" = "Run";
  30. /* Run workflow title */ "RUN_WORKFLOW_BUTTON_TITLE" = "࣮ߦ"; /* Go

    on a run title */ "GO_ON_A_RUN_BUTTON_TITLE" = "૸Δ";
  31. NSLocalizedString("RUN_WORKFLOW_BUTTON_TITLE", comment: "Run workflow title") NSLocalizedString("GO_ON_A_RUN_BUTTON_TITLE", comment: "Go on a

    run title")
  32. NSLocalizedString("Run", comment: "Run workflow title") NSLocalizedString("Run", comment: "Go on a

    run title")
  33. /* Run workflow title */ "Run" = "Run"; /* Go

    on a run title */ "Run" = "Run";
  34. /* Run workflow title */ "run-run_workflow_title" = "Run"; /* Go

    on a run title */ "run-go_on_a_run_title" = "Run";
  35. NSLocalizedString("Run", comment: "Run workflow title") NSLocalizedString("Run", comment: "Go on a

    run title") /* Run workflow title */ "run-run_workflow_title" = "Run"; /* Go on a run title */ "run-go_on_a_run_title" = "Run";
  36. GOAL #1: Effortless NSLOCALIZEDSTRING-ING

  37. GOAL #2: CONTINUOUS LOCALIZATION

  38. FETCH LOCALIZABLE.STRINGS FROM THE ‘

  39. GOALS 1. Effortless NSLocalizedString-ing via autogenerating localization keys from comments

    2. Continuous localization via loading strings from the ‘
  40. NSLocalizedString WFLocalizedString

  41. $ genstrings -o en.lproj -s WFLocalizedString

  42. $ genstrings -o en.lproj -s WFLocalizedString

  43. Script IT

  44. IN SWIFT —

  45. WHY SWIFT? 1. Familiar 2. Or new! 3. Fewer language

    dependencies 4. Type-safe 5. But still feels light-weight & script friendly 6. Fun!
  46. THE PLAN 1. Get all the .swift, .m, .mm files

    2. Parse out all of the WFLocalizedStrings from each file 3. Write them all out to Localizable.strings
  47. STRING PARSING

  48. [self setTitle:WFLocalizedString(@"Create Workflow") forState:UIControlStateNormal]; [self setTitle:WFLocalizedStringWithDescription(@"Create Workflow", @"Button") forState:UIControlStateNormal];

  49. REGULAR EXPRESSIONS !

  50. NSREGULAR EXPRESSIONS ! !

  51. "WFLocalizedString\\(@\"([^\"]*)\"\\)"

  52. "WFLocalizedStringWithDescription\\(@\"([^ \"]*)\", @\"([^\"]*)\"\\)"

  53. func getMatches(in line: String) throws -> [TextCheckingResult] { let pattern

    = "WFLocalizedString\\(@\"([^\"]*)\"\\)" let regex = try RegularExpression(pattern: pattern, options: []) let matches = regex.matches(in: line, options: [], range: NSRange(location: 0, length: line.utf16.count)) if matches.count > 0 { return matches } let descriptivePattern = "WFLocalizedStringWithDescription\\(@\"([^\"]*)\", @\"([^\"]*)\"\\)" let descriptiveRegex = try RegularExpression(pattern: descriptivePattern, options: []) return descriptiveRegex.matches(in: line, options: [], range: NSRange(location: 0, length: line.utf16.count)) }
  54. struct LocalizedString { let string: String let description: String? }

    func getLocalizedStrings(in line: String) throws -> [LocalizedString] { let matches = try getMatches(in: line) return matches.map { let line = line as NSString let string = line.substring(with: $0.range(at: 1)) var description: String? = nil if $0.numberOfRanges > 2 { description = line.substring(with: $0.range(at: 2)) } return LocalizedString(string: string, description: description) } }
  55. PLAYGROUNDS TO THE RESCUE!

  56. DEMO

  57. Script?

  58. HELLO-WORLD.SWIFT print("Hello AltConf 2016!")

  59. XCRUN SWIFT HELLO-WORLD.SWIFT

  60. CHMOD +X HELLO-WORLD.SWIFT

  61. #!/USR/BIN/ENV XCRUN SWIFT

  62. HELLO-WORLD.SWIFT #!/usr/bin/env xcrun swift print("Hello AltConf 2016!")

  63. ./HELLO-WORLD.SWIFT

  64. DEMO

  65. WHAT NOW?

  66. Translation

  67. LESS Scriptable

  68. But lots of fun!

  69. PEOPLE ARE GREAT WITH NUANCES

  70. ͜ͷϔΞελΠϧɺͲ (WHAT DO YOU THINK OF THIS HAIRSTYLE?)

  71. None
  72. ͜ͷϔΞελΠϧɺͲ (WHAT DO YOU THINK OF THIS HAIRSTYLE?)

  73. ͏ΜɺͪΐͬͱͶ… (YEAH, A LITTLE…)

  74. None
  75. ͏ΜɺͪΐͬͱͶ… ((THAT’S REALLY BAD. YOU SHOULD GO BACK TO THE

    HAIR SALON AND HAVE IT FIXED.))
  76. Who are the best localizers?

  77. Who understands your product the most?

  78. USERS!

  79. None
  80. None
  81. None
  82. Script THE TEDIOUS PARTS. Focus ON THE NUANCED PARTS.

  83. TIPS ▸ Make all user facing strings an NSLocalizedString even

    if you don’t have plans to localize the app in the nearest future. ▸ Avoid using spaces for layout padding like @" OK" ▸ Avoid concatenating strings to make sentences.
  84. MORE READING ▸ Apple’s iOS Developer Library: Localizing Your App

    ▸ objc.io Issue 9: String Localization ▸ My Swift Summit London talk
  85. COOL PROJECTS & INSPIRATION ▸ SwiftGen by AliSoftware ▸ twine

    by mobiata
  86. GITHUB.COM/AYANONAGON/TALKS

  87. Thank you! ??? @AYANONAGON

  88. None