Is there a way to check the wording displayed on iOS?

Asked 2 years ago, Updated 2 years ago, 131 views

We are developing iOS apps that support multiple languages.
In this iOS app, I would like to have the same wording displayed by iOS displayed in multiple languages.

For example, if you want access to a photo, iOS will display the following wording.

Enter a description of the image here

Is there any way to know how the following words are displayed in multiple languages?

  • You are trying to access "Photos" from "[Application Name]
  • Select a photo...
  • Allow access to all photos
  • Do not allow

I think it's inevitable to check one by one while changing the language settings on the iPhone.

Thank you for your cooperation.

ios string

2022-09-29 20:30

1 Answers

// Obtain Bundle with the appropriate message
guardlet bundle=Bundle(path:"/System/Library/PrivateFrameworks/TCC.framework") else {
  return
}

// Obtain each language contained in the Bundle
for localization in bundle.localizations {
  print("=====\(localization)")

  // Obtain Localizable.string file paths for each language
  let fileUrl=bundle.url(
    forResource: "Localizable",
    withExtension: "string",
    subdirectory:nil,
    localization:localization
  )
  guardlet fileUrl=fileUrl,let data=try?Data(contentsOf:fileUrl)else{
    continue
  }

  // Localizable.string from property list to Dictionary
  // Easy-to-read
  letdecoder=PropertyListDecoder()
  guardlet plist=try?decoder.decode(Dictionary<String, String>.self, from:data) else {
    continue
  }
  print(plist.keys.map {#"\"\#($0)"="\#(plist[$0]!); "#}.joined (separator: "\n")
}

With the above code,

...
"REQUEST_ACCESS_SERVICE_kTCCServiceCamera"="%@" is requesting access to the camera;
"REQUEST_ACCESS_ALLOW_kTCCServiceFallDetection" = "Share data;
"REQUEST_ACCESS_SERVICE_kTCCServiceMediaLibrary"="%@" is requesting access to Apple Music, music and video history, and media libraries;
"REQUEST_ACCESS_ALLOW_kTCCServicePhotos" = "Allow access to all photos;
"REQUEST_ACCESS_DENY_kTCCServiceExposeNotification" = "Do not enable;
"REQUEST_ACCESS_DENY_kTCCServiceFocusStatus" = "Do not allow;
...
"REQUEST_ACCESS_ALLOW_kTCCServicePhotos" = "Allow Access to All Photos;
"REQUEST_ACCESS_DENY" = "Don't Allow;
"REQUEST_ACCESS_ALLOW_kTCCServiceFocusStatus" = "OK;
"REQUEST_ACCESS_SERVICE_kTCCServiceContactsFull"="%@"Would Like to Access All Your Contacts Information;
"REQUEST_ACCESS_LIMITED_kTCCServicePhotos" = "Select Photos…;
"REQUEST_ACCESS_INFO_SERVICE_kTCCServiceExposeNotification" = "Your iPhone can secure collect and share random IDs with near devices. The app can use these IDs to notify you if you may have been exposed to COVID-19. The date, duration and signature not yet." @will occur;"
"REQUEST_ACCESS_INFO_SERVICE_kTCCServiceFallDetection" = "If you choose to share, "%@"can receive data from Apple Watch if a fall is detected and follow-up in case help is needed.;
"REQUEST_ACCESS_SERVICE_kTCCServiceBluetoothAlways"="%@"Would Like to Use Bluetooth;
"REQUEST_ACCESS_DENY_kTCCServiceFocusStatus" = "Don't Allow;
...

You get this output.Now you know the text for each language that the operating system displays.

The question is which Bundle (=Framework) the text you want is in, which is

You can check if Localizable.string is present for each of the /System/Library/Frameworks/ and /System/Library/PrivateFrameworks/ and write a code that prints Localizable.string for all of the frameworks.

The code below is an addition to the code above that handles all the frameworks below the specific directory.
Use this method

dumpStrings(in:"/System/Library/Frameworks")
dumpStrings (in: "/System/Library/PrivateFrameworks")

You can see almost all the message text that the operating system outputs by calling as shown in .
(It takes a lot of time to output all the text, so it is better to filter it under appropriate conditions such as only Japanese.First of all, I just need to know which framework contains the message text.)

 func dumpString(inframeworkPath:String){
  let fm = FileManager()
  guardlet frameworks=try?fm.contentsOfDirectory(atPath:frameworkPath) else{
    return
  }
  for framework in frameworks {
    guardlet bundle=Bundle(path:"\"(frameworkPath)/\(framework)") else{
      continue
    }

    for localization in bundle.localizations {
      // It takes a lot of time, so it's like this comment-out code.
      // For now, it would be better to print only the Japanese translation.
      // guard localization == "ja" else {
      //   continue
      // }
      guard
        let localizedFiles=try?fm.contentsOfDirectory(
          atPath: "\(bundle.bundlePath)/\(localization).lproj")
      else{
        continue
      }

      for localizedFile in localizedFiles {
        guard localizedFile.hasSuffix("string") else {
          continue
        }
        print("=====\(localization)/\(localizedFile) in\(bundle.bundlePath)"")
        let fileUrl=bundle.url(
          forResource:localizedFile,
          withExtension: nil,
          subdirectory:nil,
          localization:localization
        )
        guardlet fileUrl=fileUrl,let data=try?Data(contentsOf:fileUrl)else{
          continue
        }

        letdecoder=PropertyListDecoder()
        guardlet plist=try?decoder.decode(Dictionary<String, String>.self, from:data) else {
          continue
        }
        print(plist.keys.map {#"\"\#($0)"="\#(plist[$0]!); "#}.joined (separator: "\n")
      }
    }
  }
}


2022-09-29 20:30

If you have any answers or tips


© 2024 OneMinuteCode. All rights reserved.