Slide 1

Slide 1 text

@FromAtom ʮUI͸ӳޠͳͷʹΞϓϦ಺ϦϯΫ͸೔ຊޠͩʂʯΛ๷͙ίπ

Slide 2

Slide 2 text

• ϐΫγϒגࣜձࣾ • iOSΞϓϦ෼໺ςοΫϦʔυ • ޷͖ͳSwift͸ guard @FromAtom

Slide 3

Slide 3 text

͜͏͍͏ࣄ͋Γ·͢ΑͶʁ

Slide 4

Slide 4 text

ϔϧϓ͕ผݴޠʹͳͬͯ͠·͏ ※ಈը

Slide 5

Slide 5 text

ӳޠ༻ϔϧϓϖʔδ https://sites.google.com/view/url-l18n-demo/help-english ೔ຊޠ༻ϔϧϓϖʔδ https://sites.google.com/view/url-l18n-demo/help-japanese

Slide 6

Slide 6 text

੩తURLΛ؅ཧ͢Δ࿩

Slide 7

Slide 7 text

switch Locale.current.identifier { case "ja_JP": // ೔ຊޠURL case "en_US": // ӳޠURL default: // ͦͷଞݴޠURL } ύοͱࢥ͍࣮ͭ͘૷͸Locale.current • ରԠݴޠ͕૿͑ͨΒʁ • "en_JP" ΍ "ja_US" ͸ʁ

Slide 8

Slide 8 text

URL΋จࣈྻͳͷͰ຋༁͠Α͏

Slide 9

Slide 9 text

String Catalogs

Slide 10

Slide 10 text

No content

Slide 11

Slide 11 text

enum ExternalStaticLinks: String, CaseIterable, Sendable { case helpPage = "HelpPageURL" public var localizationValue: String.LocalizationValue { .init(rawValue) } public var url: URL { .init(string: urlString)! } public var urlString: String { .init(localized: localizationValue) } } let url = ExternalStaticLinks.helpPage.url

Slide 12

Slide 12 text

σόοά͢Δ

Slide 13

Slide 13 text

No content

Slide 14

Slide 14 text

※ಈը

Slide 15

Slide 15 text

🧐 URL΍ରԠݴޠ͕૿͑ͨΒखಈνΣοΫେมͰ͸ʁ

Slide 16

Slide 16 text

😀 TestΛॻ͖·͠ΐ͏

Slide 17

Slide 17 text

enum SupportLanguages: String, CaseIterable { case en = "en" case ja = "ja" var bundle: Bundle? { guard let path = Bundle.main.path(forResource: rawValue, ofType: "lproj") else { return nil } return Bundle(path: path) } } final class LinkLocalizeSampleTests: XCTestCase { func test_HelpページのURLが全言語で想定通りに返ってくる() throws { let localizationValue = ExternalStaticLinks.helpPage.localizationValue for lang in SupportLanguages.allCases { let urlString = String(localized: localizationValue, bundle: lang.bundle) switch lang { case .en: XCTAssertEqual(urlString, "https://sites.google.com/view/url-l18n-demo/help-english") case .ja: XCTAssertEqual(urlString, "https://sites.google.com/view/url-l18n-demo/help-japanese") } XCTAssertNotNil(URL(string: urlString)) } } }

Slide 18

Slide 18 text

enum SupportLanguages: String, CaseIterable { case en = "en" case ja = "ja" var bundle: Bundle? { guard let path = Bundle.main.path(forResource: rawValue, ofType: "lproj") else { return nil } return Bundle(path: path) } } final class LinkLocalizeSampleTests: XCTestCase { func test_HelpページのURLが全言語で想定通りに返ってくる() throws { let localizationValue = ExternalStaticLinks.helpPage.localizationValue for lang in SupportLanguages.allCases { let urlString = String(localized: localizationValue, bundle: lang.bundle) switch lang { case .en: XCTAssertEqual(urlString, "https://sites.google.com/view/url-l18n-demo/help-english") case .ja: XCTAssertEqual(urlString, "https://sites.google.com/view/url-l18n-demo/help-japanese") } XCTAssertNotNil(URL(string: urlString)) } } }

Slide 19

Slide 19 text

enum SupportLanguages: String, CaseIterable { case en = "en" case ja = "ja" var bundle: Bundle? { guard let path = Bundle.main.path(forResource: rawValue, ofType: "lproj") else { return nil } return Bundle(path: path) } } final class LinkLocalizeSampleTests: XCTestCase { func test_HelpページのURLが全言語で想定通りに返ってくる() throws { let localizationValue = ExternalStaticLinks.helpPage.localizationValue for lang in SupportLanguages.allCases { let urlString = String(localized: localizationValue, bundle: lang.bundle) switch lang { case .en: XCTAssertEqual(urlString, "https://sites.google.com/view/url-l18n-demo/help-english") case .ja: XCTAssertEqual(urlString, "https://sites.google.com/view/url-l18n-demo/help-japanese") } XCTAssertNotNil(URL(string: urlString)) } } } // => HelpPageURL

Slide 20

Slide 20 text

enum SupportLanguages: String, CaseIterable { case en = "en" case ja = "ja" var bundle: Bundle? { guard let path = Bundle.main.path(forResource: rawValue, ofType: "lproj") else { return nil } return Bundle(path: path) } } final class LinkLocalizeSampleTests: XCTestCase { func test_HelpページのURLが全言語で想定通りに返ってくる() throws { let localizationValue = ExternalStaticLinks.helpPage.localizationValue for lang in SupportLanguages.allCases { let urlString = String(localized: localizationValue, bundle: lang.bundle) switch lang { case .en: XCTAssertEqual(urlString, "https://sites.google.com/view/url-l18n-demo/help-english") case .ja: XCTAssertEqual(urlString, "https://sites.google.com/view/url-l18n-demo/help-japanese") } XCTAssertNotNil(URL(string: urlString)) } } }

Slide 21

Slide 21 text

enum SupportLanguages: String, CaseIterable { case en = "en" case ja = "ja" var bundle: Bundle? { guard let path = Bundle.main.path(forResource: rawValue, ofType: "lproj") else { return nil } return Bundle(path: path) } } final class LinkLocalizeSampleTests: XCTestCase { func test_HelpページのURLが全言語で想定通りに返ってくる() throws { let localizationValue = ExternalStaticLinks.helpPage.localizationValue for lang in SupportLanguages.allCases { let urlString = String(localized: localizationValue, bundle: lang.bundle) switch lang { case .en: XCTAssertEqual(urlString, "https://sites.google.com/view/url-l18n-demo/help-english") case .ja: XCTAssertEqual(urlString, "https://sites.google.com/view/url-l18n-demo/help-japanese") } XCTAssertNotNil(URL(string: urlString)) } } }

Slide 22

Slide 22 text

enum SupportLanguages: String, CaseIterable { case en = "en" case ja = "ja" var bundle: Bundle? { guard let path = Bundle.main.path(forResource: rawValue, ofType: "lproj") else { return nil } return Bundle(path: path) } } final class LinkLocalizeSampleTests: XCTestCase { func test_HelpページのURLが全言語で想定通りに返ってくる() throws { let localizationValue = ExternalStaticLinks.helpPage.localizationValue for lang in SupportLanguages.allCases { let urlString = String(localized: localizationValue, bundle: lang.bundle) switch lang { case .en: XCTAssertEqual(urlString, "https://sites.google.com/view/url-l18n-demo/help-english") case .ja: XCTAssertEqual(urlString, "https://sites.google.com/view/url-l18n-demo/help-japanese") } XCTAssertNotNil(URL(string: urlString)) } } } default: ͸࢖Θͳ͍

Slide 23

Slide 23 text

enum SupportLanguages: String, CaseIterable { case en = "en" case ja = "ja" var bundle: Bundle? { guard let path = Bundle.main.path(forResource: rawValue, ofType: "lproj") else { return nil } return Bundle(path: path) } } final class LinkLocalizeSampleTests: XCTestCase { func test_HelpページのURLが全言語で想定通りに返ってくる() throws { let localizationValue = ExternalStaticLinks.helpPage.localizationValue for lang in SupportLanguages.allCases { let urlString = String(localized: localizationValue, bundle: lang.bundle) switch lang { case .en: XCTAssertEqual(urlString, "https://sites.google.com/view/url-l18n-demo/help-english") case .ja: XCTAssertEqual(urlString, "https://sites.google.com/view/url-l18n-demo/help-japanese") } XCTAssertNotNil(URL(string: urlString)) } } }

Slide 24

Slide 24 text

😀 ͜ΕͰ׬ᘳʂ

Slide 25

Slide 25 text

😭 Ͱ͸ͳ͍

Slide 26

Slide 26 text

🫠 ৽͍͠URLΛ௥Ճͯ͠ ͦͷςετهड़ΛαϘͬͨΒʁ

Slide 27

Slide 27 text

😀 ͦͷTest΋ॻ͖·͠ΐ͏

Slide 28

Slide 28 text

func test_ExternalStaticLinksを増減させたのにテストを修正していない時におちる() throws { // NOTE: - ここの数字を変えるだけでなくて、下記のテストを適切に修正すること XCTAssertEqual(ExternalStaticLinks.allCases.count, 11) } ※1: URLΛ1ͭফͯ͠ɺ1ͭ૿΍ͨ͠ͱ͖ʹ͸෼͔Βͳ͍ ※2: ݫີʹ΍ΔͳΒCIͰΧόϨοδ͕௿Լͨ͠ΒΞϥʔτΛग़͢

Slide 29

Slide 29 text

🫠 String Catalogʹ͸௥Ճ͚ͨ͠Ͳɺ ͦ΋ͦ΋ΞϓϦຊମ͕ݴޠαϙʔτͯ͠ͳ͔ͬͨΒʁ

Slide 30

Slide 30 text

func test_想定している全言語の翻訳に対応している() throws { for lang in SupportLanguages.allCases { XCTAssertNotNil(lang.bundle, "Application not translated in \(lang.longName)") } }

Slide 31

Slide 31 text

೔ຊޠɾӳޠͰผURLͳͷʹ͏͔ͬΓಉ͡URLΛొ࿥ͯ͠͠·ͬͨΒʁ ɹ⾣ UnitTestͰམͪΔ ੩తϦϯΫΛ௥Ճͨ͠ͷʹUnitTestΛ௥Ճ͠ͳ͔ͬͨΒʁ ɹ⾣ UnitTestͰམͪΔ Package.swiftͰ͸ରԠͯ͠Δݴޠ͚ͩͲΞϓϦຊମͰະରԠͩͬͨΒʁ ɹ⾣ UnitTestͰམͪΔ

Slide 32

Slide 32 text

😀 Αͦ͞͏ʂ

Slide 33

Slide 33 text

͓·͚ iOS 16+͔ΒBCP47ܗ͕ࣜѻ͑ΔΑ͏ʹͳͬͨɻ print(Locale.current.identifier) // en_JP print(Locale.current.identifier(.bcp47)) // en-JP

Slide 34

Slide 34 text

·ͱΊ • ੩తϦϯΫ΋จࣈྻͳͷͰString CatalogsͰ؅ཧ͠Α͏ • bundleΛར༻֤ͯ͠ݴޠͷ຋༁Λςετ͠Α͏ • ਓؒ͸TestΛ๨Ε͕ͪͳͷͰɺॻ͔ͤΔ࢓૊ΈΛߟ͑Α͏