Dynamic Localization

Hi. I'm trying to create dynamic localization, but I getting an error. Please, someone, help me :)

Code Block
class LocalizationManager {
  var currentBundle = Bundle.main
     
  let manager = FileManager.default
  lazy var bundlePath: URL = {
    let documents = URL(fileURLWithPath: NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first!)
    let bundlePath = documents.appendingPathComponent(RuntimeLocalizable.bundleName, isDirectory: true)
    return bundlePath
  }()
   
  public func returnCurrentBundleForLanguage(lang:String) throws -> Bundle {
    if manager.fileExists(atPath: bundlePath.path) == false {
      return Bundle(path: getPathForLocalLanguage(language: lang))!
    }
    do {
      let resourceKeys : [URLResourceKey] = [.creationDateKey, .isDirectoryKey]
      _ = try manager.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
      let enumerator = FileManager.default.enumerator(at: bundlePath ,
                              includingPropertiesForKeys: resourceKeys,
                              options: [.skipsHiddenFiles], errorHandler: { (url, error) -> Bool in
                                return true
      })!
      for case let folderURL as URL in enumerator {
        _ = try folderURL.resourceValues(forKeys: Set(resourceKeys))
        if folderURL.lastPathComponent == ("\(lang).lproj"){
          let enumerator2 = FileManager.default.enumerator(at: folderURL,
                                   includingPropertiesForKeys: resourceKeys,
                                   options: [.skipsHiddenFiles], errorHandler: { (url, error) -> Bool in
                                    return true
          })!
          for case let fileURL as URL in enumerator2 {
            _ = try fileURL.resourceValues(forKeys: Set(resourceKeys))
            if fileURL.lastPathComponent == "languages.strings" {
              return Bundle(url: folderURL)!
            }
          }
        }
      }
    } catch {
      return Bundle(path: getPathForLocalLanguage(language: lang))!
    }
    return Bundle(path: getPathForLocalLanguage(language: lang))!
  }
   
  private func getPathForLocalLanguage(language: String) -> String {
    return Bundle.main.path(forResource: language, ofType: "lproj")!
  }
   
  func setCurrentBundle(forLanguage:String){
    do {
      currentBundle = try returnCurrentBundleForLanguage(lang: forLanguage)
    }catch {
      currentBundle = Bundle(path: getPathForLocalLanguage(language: "en"))!
    }
  }
}
/////
import Foundation
public final class RuntimeLocalizable {
   
  public typealias LanguageKey = String
  public typealias Language = Dictionary<String, String>
  public typealias Translations = Dictionary<LanguageKey, Language>
   
  let tableName: String
  let translations: Translations
   
  static let bundleName = "RuntimeLocalizable.bundle"
  let manager = FileManager.default
   
  lazy var bundlePath: URL = {
    let documents = URL(fileURLWithPath: NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first!)
    print("\n   DIRECTORY: \(documents.absoluteString)\n")
     
    let bundlePath = documents.appendingPathComponent(RuntimeLocalizable.bundleName, isDirectory: true)
    return bundlePath
  }()
   
  public init(translations: Translations, name: String) {
    tableName = name
    self.translations = translations
  }
   
  func clean() throws {
    // TODO: There can be multiple table names in the same Bundle. So only remove the bundle if there is no more string files.
    var langFiles: Dictionary<String, Int> = [:]
     
    for item in manager.enumerator(at: bundlePath, includingPropertiesForKeys: nil, options: [.skipsPackageDescendants])! {
      print(item)
    }
    if manager.fileExists(atPath: bundlePath.path) {
      try manager.removeItem(at: bundlePath)
    }
  }
   
  public func bundle() throws -> Bundle {
     
    if manager.fileExists(atPath: bundlePath.path) == false {
      try manager.createDirectory(at: bundlePath, withIntermediateDirectories: true, attributes: nil)
    }
     
    for language in translations {
      let lang = language.key
      let langPath = bundlePath.appendingPathComponent("\(lang).lproj", isDirectory: true)
      if manager.fileExists(atPath: langPath.path) == false {
        try manager.createDirectory(at: langPath, withIntermediateDirectories: true, attributes: nil)
      }
       
      let sentences = language.value
      let res = sentences.reduce("", { $0 + "\"\($1.key)\" = \"\($1.value)\";\n" })
       
      let filePath = langPath.appendingPathComponent("\(tableName).strings")
      let data = res.data(using: .utf32)
      manager.createFile(atPath: filePath.path, contents: data, attributes: nil)
       
       
      print(res)
    }
     
    let localBundle = Bundle(url: bundlePath)!
    return localBundle
  }
}
///////////
func localized() -> String {
    return NSLocalizedString("entertainment_header", tableName: "", bundle: LocalizationManager().currentBundle, value: "", comment: "")
  }



Answered by Claude31 in 658808022
It would help if you:
  • described precisely the error you get

  • posted the log message if any

  • explain where in code error occurs

  • give information on test case (simulator ? Device ? Version of Xcode…)

Accepted Answer
It would help if you:
  • described precisely the error you get

  • posted the log message if any

  • explain where in code error occurs

  • give information on test case (simulator ? Device ? Version of Xcode…)

When i setting bundle name like this:
Code Block
static let bundleName = "RuntimeLocalizable"

en.lproj ar.lproj ... is creating

but now
Code Block
static let bundleName = "RuntimeLocalizable.bundle"

lang files aren't created. RuntimeLocalizable.bundle file is creating
i can't see any changes on textlabel's value. error message is en.lproj... path is null (because, files wasn't created)


What do you want exactly with Dynamic Localization ?
  • add a new language programmatically ?

  • select the language from the app, directly ?

If you want to localise 'on the fly':
https://developer.apple.com/forums/thread/129946
Claude31, I'm getting language data from server and writing to lproj directories, all files (en.lproj, tr.lproj...) successfully writing but when i writing this line of code, i can't see any changes, and i getting error message for second line: Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value
Code Block
let bundlePath = Bundle.main.path(forResource: "en", ofType: "lproj")
let bundle   = Bundle(path: bundlePath!)!
return NSLocalizedString("entertainment_header", tableName: "", bundle: bundle, value: "", comment: "")

Did you test bundlePath ?
Add a print and tell what you get
Code Block
let bundlePath = Bundle.main.path(forResource: "en", ofType: "lproj")
        print(bundlePath)
let bundle = Bundle(path: bundlePath!)!

it returned - nil

it returned - nil

So, you have to check if you effectively copied the file in the Bundle, with the correct name.
All lang files are placed like this: RuntimeLocalizade -> en.lpoj -> languages.strings

and When I setts Bundle Name is RuntimeLocalizable.bundle - weren't created any file only created-RuntimeLocalizable.bundle

but, i changes Bundle Name to RuntimeLocalizable - all of lang files were created like this

in RuntimeLocalizable
  • en.lproj -> languages.strings

  • tr.lproj -> languages.strings


Dynamic Localization
 
 
Q