IOS App Localization Process at Vivid Money

Hello! If your application supports different languages, then you probably encountered problems related to localization: errors in writing keys, missing values ​​for languages, the need to rebuild the application in case of emergency translation edits. Not the most pleasant moments of development, is it?





This article will discuss how localization works in Vivid.Money: we will tell you which tool for localization you chose, what problems you encountered and how they were solved.





For a better understanding of the specifics of the project, you can familiarize yourself with this material, and we suggest starting this article without further ado.





Which localization method did you choose

Our localization requirements include the following points:





  • Synchronization between platforms (iOS, Android, Backend) for a single source of truth;





  • Checking the correct spelling of the keys used during compilation to eliminate the possibility of making a typo in the key name;





  • No need for developers to independently introduce localizations for different languages ​​in order for developers to spend more time doing what they should - to implement features;





  • Ease of interaction with translators;





  • The ability to change key values ​​without rebuilding the application.





Apple .strings .stringsdict. .strings (“”: “”), . .stringsdict plural plist. , NSLocalizedString. Apple .





  , : , , , . XLIFF ( , XML), . , - .





, , , . , , , :









Lokalise





Phrase





OneSky





POEditor









+





-





-





-









+





+





-





+





plural





+





+





+/-





+





SDK





+





+





+





-









+





-





-





-





/





+





-





-





-









+





-





-





-









+





+/-





-





-









https://lokalise.com/pricing





https://phrase.com/pricing/





https://www.oneskyapp.com/pricing/





https://poeditor.com/pricing/





Crowdin, . , - , . Lokalise, , . , , .





lokalise , , , . : 





  • ;





  • Fastlane, ;





  • API/CLI;





  • SDK.





Lokalise .





, SDK. : , , SDK. , , .





API, Lokalise , . .





, Lokalise SDK, . : . , Lokalise SDK , , , .





(debug/release), .





.strings .stringsdict plutil (property list utility), : .. Ruby:





def self.valid_bundle?(path)
        puts 'Validating localization bundle...'
 
        strings = Dir["#{path}/Contents/Resources/*.lproj/*.strings"]
        stringsdict = Dir["#{path}/Contents/Resources/*.lproj/*.stringsdict"]
        
        is_valid = true
 
        (strings + stringsdict).each do |path|
          stdout, stderr, status = Open3.capture3("plutil -lint #{path}")
 
          unless status.exitstatus == 0
            is_valid = false
            line = stderr.strip[/on line ([0-9]*)/, 1]
 
            puts "***********************************************".red
            puts "Found the invalid string in file at path: #{path}".red
            puts "The invalid string: #{File.readlines(path)[line.to_i-1].strip}".red
          end
        end
 
        puts "***********************************************".red unless is_valid
 
        is_valid
      end

      
      



Localisation, .





,   - . LocalizationFetcher : . , , - . , , Documents, . 





, , , Localise.bundle. : Documents .lproj Localizable.strings Localizable.stringsdict Localizable.nocache.strings Localizable.nocache.stringsdict, . “Localizable.nocache” tableName localizedString(forKey:value:table:) , .





, : , , , .





R.swift , .., -, , ( ), -, . .





: Localisation.swift , Localizable.strings Localizable.stringsdict .





?





public static let localization_var = "localization_key".localized
public static func localization_method(_ value1: String) -> String {
    "localization_method_key".localized(with: value1)
}
      
      



localized :





var localized: String {
    let localLocalisation = Self.localLocaliseBundle?.localizedString(
        forKey: self,
        value: nil,
        table: nil
    )
 
    let serverLocalisation = Self.makeServerLocaliseBundle()?.localizedString(
        forKey: self,
        value: localLocalisation,
        table: “Localizable.nocache”
    )
 
    return serverLocalisation ?? localLocalisation ?? self
}
      
      



  • ;





  • , Documents, ;





  • , , , .





, (localized(with:)), :





func localized(with parameters: CVarArg...) -> String {
    let correctLocalizedString: String = {
        if localized.starts(with: "%#@") {
            return localized
        }
 
        return localized
            .replacingOccurrences(of: "%s", with: "%@")
            .replacingOccurrences(
                of: "(%[0-9])\\$s",
                with: "$1@",
                options: .regularExpression
            )
    }()
 
    guard !correctLocalizedString.isEmpty else {
        return self
    }
 
    return String(format: correctLocalizedString, arguments: parameters)
}
      
      



:





  • plural ;





  • , Lokalise, ;





  • , ;





  • , , .





: -, Localizable.stringsdict , ; -, , , plural . , localizedString(forKey:value:table:) plural , Localizable.stringsdict, __NSLocalizedString. plural . , .regularExpression replacingOccurencies(of:with:options:) String , , __NSLocalizedString.





, : 





  1. ;





  2. Lokalise , ;





  3. , , , debug , Localisation.swift;





  4. , , , ;





  5. Lokalise;





  6. , , production Lokalise , ;





  7. production Lokalise;





  8. .





production Lokalise Gitlab , , , , , .





, .





iOS Android

, , , iOS Android -. , iOS %@, Android %s. , , Android first Lokalise, iOS .





, - . , . . , .





, , .





, 2 “” - debug production. debug , , production - , debug production.





, , production . , - . : , Localisation.swift, . , , .





, Lokalise, - . , .





, , , debug production , . , , , .





, , - , . That’s all, Folks!








All Articles