Traduire une application iOS en over-the-air avec Phrase Strings

Si vous voulez vous assurer que tous vos utilisateurs d’application obtiennent le bon texte, la fonctionnalité over-the-air de Phrase peut vous aider à publier vos mises à jour de traduction instantanément.

Notre équipe application peut se passer des développeurs (nous libérant un temps précieux) lors du déploiement de nouvelles traductions dans notre application. Nous pouvons également nous passer de l’approbation de l’App Store en cas de mise à jour du contenu localisé de notre application. Voyons comment nous pouvons bénéficier de ces avantages dans notre application iOS grâce à la fonctionnalité over-the-air de Phrase. Nous allons ajouter Phrase over-the-air à une application fonctionnelle, le tout depuis la console Phrase et notre projet XCode.

Notre application

Nous reprenons là où nous nous étions arrêtés dans notre précédent article (en anglais) iOS App Localization with Phrase. Dans ce tutoriel, nous avons pris une application et nous l’avons connectée à Phrase pour rationaliser notre flux de travaux de localisation. Nous supposons que vous avez lu cet article, et/ou que vous avez déjà connecté Phrase à votre application. Dans le cas contraire, nous vous recommandons vivement de consulter cet article avant de continuer.

Application de démonstration | PhraseShort Circuit, notre application de démonstration, une liste de morceaux de musique électronique

Notre petite application de démonstration liste des morceaux de musique électronique, ainsi que leurs interprètes et leurs dates de sortie. C’est une application simple à deux écrans avec un UITableViewController qui présente une liste des morceaux, et dispose d’un écran de détails pour afficher les informations d’un morceau unique.

🔗 Ressources » Si vous souhaitez faire le travail en parallèle, vous pouvez récupérer le projet de démarrage sur Github. Le projet terminé se trouve près du bas de l’article.

Préparation

D’accord, commençons à préparer notre application pour les traductions over-the-air. Dans notre application iOS, nous allons déplacer nos chaînes localisées des storyboards vers un bon vieux Localizable.strings. Nous ajouterons ensuite le support de traduction over-the-air à notre projet Phrase. Pour compléter notre préparation, nous installerons le SDK iOS de Phrase, qui synchronise les traductions de notre application avec notre projet Phrase over-the-air.

Déplacer les traductions de notre application des Storyboards vers Localizable.strings

Les traductions Phrase over-the-air fonctionneront parfaitement avec les fichiers storyboard .strings. Cependant, pour plus de simplicité, nous allons ici mettre toutes nos chaînes traduisibles dans un seul fichier Localizable.strings. Il s’agit d’un simple remaniement de Main.strings à Localizable.strings, alors allons-y.

📖 Pour aller plus loin » Si vous souhaitez en savoir plus sur la localisation des storyboards iOS, consultez notre guide, Internationalisation iOS : internationaliser les storyboards dans Xcode

Commençons par les titres des éléments de navigation de notre écran. Nous pouvons simplement remplacer les titres définis dans nos storyboards en définissant le champ title dans nos contrôleurs de vue. Notre écran d’accueil, qui présente la liste des artistes, est connecté à TrackListViewController. Mettons à jour ce contrôleur maintenant.

class TrackListViewController: UIViewController {
   // ...
    override func viewDidLoad() {
        super.viewDidLoad()
        title = NSLocalizedString("trackListTitle", comment: "")
        // ...
    }
    // ...
}

Nous utilisons le NSLocalizedString habituel pour récupérer notre traduction depuis Localizable.strings. Assurons-nous que la chaîne de traduction existe dans la version anglaise de Localizable.strings.

"copyright" = "Copyright © %@ %@. All rights reserved";
"trackListTitle" = "Short Circuit";

Ceci étant fait, notre application aura exactement le même aspect lorsqu’elle sera exécutée en anglais. Nous ajouterons les paramètres régionaux autres qu’anglais un peu plus tard. Terminons d’abord de déplacer nos traductions hors de nos storyboards.
Pour nos étiquettes, nous devons nous assurer que nous disposons d’outlets depuis nos storyboards vers leurs contrôleurs respectifs.

Après avoir connecté nos étiquettes, nous répétons simplement le même processus d’appel à NSLocalizedString pour leur fournir les chaînes traduites. Bien sûr, nous ajoutons nos nouvelles chaînes dans notre fichier anglais Localizable.strings.
Voici à quoi devrait ressembler TrackDetailsViewController, qui aide à afficher les informations d’un morceau, après avoir déplacé nos traductions storyboard :

import UIKit
class TrackDetailsViewController: UIViewController {
    @IBOutlet weak var trackNameHeaderLabel: UILabel!
    @IBOutlet weak var artistNameHeaderLabel: UILabel!
    // ...
    var track: Track?
    override func viewDidLoad() {
        super.viewDidLoad()
        title = NSLocalizedString("trackDetailsTitle", comment: "")
        if let track = track {
            trackNameHeaderLabel.text =
                getLocalizedHeaderText(key: "trackNameHeader")
            artistNameHeaderLabel.text =
                getLocalizedHeaderText(key: "artistNameHeader")
            releaseDateHeaderLabel.text =
                getLocalizedHeaderText(key: "releaseDateHeader")
            // ...
        }
    }
    // ...
    fileprivate func getLocalizedHeaderText(key: String) -> String {
        return NSLocalizedString(key, comment: "")
            .localizedUppercase
    }
}

La fonction getLocalizedHeaderText est simplement un petit utilitaire que nous utilisons pour fournir un formatage en majuscules localisé à notre texte d’étiquette traduit.
Après avoir ajouté nos chaînes de traduction à notre Localizable.strings, notre application a exactement le même aspect qu’auparavant.

Appli avec refactorisation réussie | PhraseRefactorisation réussie

Nous pouvons maintenant retirer complètement nos fichiers de traduction storyboard de notre projet XCode. Nous le faisons en sélectionnant le fichier storyboard dans le navigateur de projet et en décochant chaque paramètre régional sous l’en-tête Localizations dans File inspector.

📖 Pour aller plus loin » Si vous travaillez beaucoup avec des storyboards iOS, nous vous invitons à consulter notre article (en anglais) Automate iOS Storyboard Localization.

Mise à jour de nos traductions avec Phrase

Nous devons maintenant mettre à jour nos traductions Localizable.strings pour nos langues non-source (autres qu’anglais dans mon cas). Nous le faisons afin que nos utilisateurs non anglophones puissent voir nos étiquettes de mise à jour dans leur langue. Nous pouvons, bien sûr, utiliser notre flux de travaux habituel Phrase pour traduire nos étiquettes.
Tout d’abord, nous allons charger notre nouvelle source en utilisant $ phrase push depuis la ligne de commande.
Nos nouvelles clés de traduction devraient maintenant apparaître sur la console web de Phrase. Une fois que nos traducteurs ont traduit les clés dans tous les paramètres régionaux que notre application prend en charge, nous pouvons utiliser $ phrase pull depuis la ligne de commande pour obtenir nos fichiers Localizable.strings mis à jour. Désormais, lorsque nous exécutons notre application dans un paramètre linguistique non source (arabe dans mon cas), nous constatons que nos vues sont traduites comme prévu.

Application de démonstration traduite | PhraseTout va bien dans tous les paramètres régionaux

📖 Pour aller plus loin » Consultez Localiser une application iOS avec Phrase pour en savoir plus sur le flux de travaux Phrase de base pour iOS.

Maintenant que nous avons déplacé nos chaînes de traduction storyboard vers des fichiers Localizable.strings, nous pouvons passer à la configuration des traductions over-the-air dans notre projet Phrase.

Ajout d’une distribution over-the-air dans Phrase

Nous allons préparer notre projet Phrase pour les traductions over-the-air. Dans notre console web Phrase, nous allons ouvrir le menu déroulant de notre organisation dans la barre de navigation supérieure et sélectionner Integrations.

Ouverture du menu Intégrations dans Phrase | Phrase OTA se trouve sous Intégrations

Cela ouvre la page Phrase Integrations. En regard de la ligne Over the Air (OTA), nous cliquons sur le bouton Configure.

Configuration d'OTA | PhraseToujours plus de nouvelles intégrations Phrase

La page Over the Air s’ouvre, nous invitant à créer une distribution. Une distribution est une configuration OTA de projet qui cible une ou plusieurs plateformes et inclut des options de secours.

Créer une distribution pour l'application de démonstration | PhraseNous allons créer une distribution OTA pour notre application iOS

Cliquez sur le bouton Create distribution pour ouvrir la boîte de dialogue Add distribution.

Options de distribution d'appli | PhraseOptions permettant d’ajouter d’une distribution

Pour notre projet actuel, nous pouvons donner à la distribution le nom de notre choix, sélectionner le projet Phrase associé à notre application iOS, et cocher la case de la plateforme ios. Nous pouvons laisser les options de secours par défaut telles qu’elles sont, car elles conviennent à nos besoins. Cliquez sur le bouton Save pour créer la distribution.

💡Pour information » Vous pouvez modifier les paramètres de votre distribution à tout moment en revenant à la page Over the Air.

Ajout de notre première version OTA

Pour travailler avec le SDK iOS sans voir de résultat d’échec lorsque nous essayons de mettre à jour nos traductions dans l’application, nous devons disposer d’une version OTA. Une version est essentiellement un instantané de nos traductions Phrase que nous pouvons tester et déployer dans notre application iOS over-the-air. Nous parlerons des versions plus tard. Pour l’instant, créons une première version pour commencer à utiliser le SDK.
Nous commençons par cliquer sur le bouton Create release en bas de la page de notre distribution.

Bouton Create release | Phrase
Libérez-moi

Cela ouvre la boîte de dialogue Add release. Ajoutez une description pour identifier facilement la version plus tard, laissez tout le reste tel quel et cliquez sur Save.

Menu Add release | PhraseUne version est un instantané des traductions que nous pouvons déployer

Vous devriez maintenant voir une entrée sous Releases près du bas de la page de notre distribution.
Maintenant que nous avons une distribution OTA et une première version, nous pouvons configurer OTA dans notre application iOS.

Installation du SDK iOS de Phrase

Nous aurons besoin que SDK Phrase soit installé pour utiliser OTA. Nous pouvons le faire via CocoaPods, Carthage, ou manuellement.
Pour installer le SDK via CocoaPods, il faut que CocoaPods soit installé sur notre Mac. Vous pouvez voir les instructions d’installation sur le site web de CocoaPods. En supposant que CocoaPods est installé, nous pouvons naviguer vers le répertoire racine du projet (celui qui contient notre fichier .xcodeproj) et exécuter la commande suivante depuis le terminal.

$ pod init

Cela crée un Podfile dans notre répertoire racine. Nous allons ouvrir ce fichier dans notre éditeur de code préféré et ajouter une ligne pour qu’il ressemble à ceci :

target 'phraseapp-demo' do
  use_frameworks!
  # Pods for phraseapp-demo
  pod 'PhraseSDK'
end

Cette modification étant enregistrée, nous pouvons exécuter ce qui suit depuis notre terminal :

$ pod install

Si tout s’est bien passé, nous devrions avoir reçu une sortie indiquant que Phrase a été installé. Après cela, nous pouvons fermer toutes les fenêtres XCode que nous avons ouvertes et ouvrir le fichier .xcworkspace que CocoaPods a ajouté à la racine de notre projet après l’installation de Phrase.

Configurer Phrase dans notre application iOS

Le SDK étant installé, nous allons le connecter à notre application. Tout d’abord, jetons un coup d’œil rapide à deux méthodes principales proposées par le SDK Phrase :
Phrase.shared.setup(distributionID:environmentSecret:timeout:) initialise l’objet singleton Phrase partagé, en prenant nos informations d’identification et un paramètre de délai d’attente facultatif.
Phrase.shared.updateTranslations(translationResult:) met à jour manuellement les traductions dans l’application, en récupérant les plus récentes depuis les serveurs Phrase si une nouvelle version existe. updateTranslations prend une fermeture d’échappement facultative, translationResult, que nous pouvons fournir si nous voulons agir lorsque la requête de mise à jour est terminée.

Nous allons ajouter une classe OTATranslations qui fournit un léger wrapper autour de PhraseApp.

import PhraseSDK
class OTATranslations {
    static let shared = OTATranslations()
    private init() {
        #if DEBUG
        Phrase.shared.debugMode = true
        #endif
        let config: PList? = loadConfig()
        if let config = config {
            #if DEBUG
            let environmentTokenKey: String = "devToken"
            #else
            let environmentTokenKey: String = "prodToken"
            #endif
            Phrase.shared.setup(
                distributionID: config.getValue(withKey: "distributionID"),
                environmentSecret: config.getValue(withKey: environmentTokenKey),
                timeout: config.getValue(withKey: "timeout")
            )
        }
    }
    func updateTranslations(onUpdateComplete: (() -> Void)? = nil) {
        do {
            try Phrase.shared.updateTranslations { result in
                switch result {
                case .success(let updated):
                    if updated {
                        printIfDebug("Translations updated successfully")
                        onUpdateComplete?()
                    } else {
                        printIfDebug("No new translations found")
                    }
                case .failure:
                    printIfDebug("Failure updating translations")
                }
            }
        } catch {
            printIfDebug("Error updating translations: \(error)")
        }
    }
    fileprivate func loadConfig() -> PList? {
        do {
            return try PList(pListResource: "PhraseApp")
        } catch {
            printIfDebug(
                "Error loading PhraseApp configuration from plist, \(error)")
        }
        return nil
    }
}

💡Pour information » Phrase.shared.debugMode est un indicateur qui rend le SDK Phrase plus bavard, en affichant plus de détails dans la console, lorsqu’il est activé. Nous l’activons lorsque nous exécutons une version de débogage de notre application.
💡Pour information » La fonction printIfDebug(_:) est un helper personnalisé qui affiche simplement la chaîne donnée dans la console si l’indicateur #DEBUG est activé.

Nous fournissons un simple objet singleton OTATranslations.shared à utiliser dans notre application. Notre objet est initialisé en tirant les valeurs de configuration de Phrase d’un PhraseApp.plist et en les fournissant à PhraseApp.setup. Pour que cela fonctionne, nous devons créer la classe PhraseApp.plist dans notre projet XCode. Nous allons le faire faire maintenant. Cela devrait se présenter de la manière suivante.

Clés | PhraseAssurez-vous que vos clés correspondent à celles-ci

Voici une version texte si vous voulez ⌘-C.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>distributionID</key>
    <string>your_distribution_id</string>
    <key>devToken</key>
    <string>your_distribution_development_secret</string>
    <key>prodToken</key>
    <string>your_distribution_production_secret</string>
    <key>timeout</key>
    <integer>10</integer>
</dict>
</plist>

✋🏽 Attention » Vous voudrez peut-être ajouter votre fichier PhraseApp.plist à votre .gitignore pour protéger vos clés.
💡Pour information » La classe PList que nous utilisons pour charger les valeurs de notre fichier .plist est une classe utilitaire personnalisée.

Les valeurs pour distributionID, devToken et prodToken peuvent être trouvées sur la page de la distribution OTA dans la console web Phrase. Nous pouvons naviguer vers l’onglet Overview du tableau de bord de notre projet, puis cliquer sur le bouton View distributions sous la section Over the Air. Cela ouvre la page Over the Air. Depuis cette page, nous pouvons trouver et cliquer sur la distribution OTA de notre projet dans la liste Distributions. Une fois sur la page de la distribution spécifique, nous pouvons trouver les valeurs Distribution ID, Development secret et Production secret à copier-coller dans notre fichier .plist. Bien sûr, nous utilisons « jeton » au lieu de « secret » dans notre .plist, mais vous l’avez probablement déjà compris.

Copier les identifiants et jetons secrets | PhraseCopiez-collez vos identifiants et jetons secrets

Maintenant que nous avons connecté le SDK Phrase à notre classe OTATranslations, nous allons l’utiliser dans AppDelegate.swift.

import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
    var window: UIWindow?
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        OTATranslations.shared.updateTranslations()
        return true
    }
}

Dans la méthode application(_:didFinishLaunchingWithOptions:) d’AppDelegate, qui s’exécute au démarrage de notre application, nous mettons manuellement à jour nos traductions over-the-air. Cela récupère toutes les nouvelles traductions en arrière-plan. Cependant, notre configuration actuelle ne mettra pas à jour l’interface utilisateur immédiatement. Les mises à jour de traduction qui sont récupérées lors de ce lancement d’application apparaîtront à l’utilisateur lors du lancement suivant de l’application. C’est souvent le comportement attendu : avoir de nouvelles chaînes de traduction qui apparaissent pendant que l’utilisateur utilise l’application pourrait être une expérience utilisateur étrange et potentiellement déstabilisante.

Rafraîchir manuellement l’interface utilisateur après avoir récupéré de nouvelles traductions

Cependant, en fonction de nos exigences, nous pourrions avoir besoin de rafraîchir l’interface utilisateur de l’application immédiatement après avoir récupéré une mise à jour de traduction. Nous pouvons le faire en utilisant le paramètre de fermeture de secours que nous avons fourni dans updateTranslations(onUpdateComplete:).

import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
    var window: UIWindow?
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        OTATranslations.shared.updateTranslations() {
            AppDelegate.reloadRootViewController()
        }
        return true
    }
    static func reloadRootViewController() {
        DispatchQueue.main.async {
            let storyboard = UIStoryboard.init(name: "Main", bundle: nil)
            let appDelegate = UIApplication.shared.delegate as! AppDelegate
            appDelegate.window?.rootViewController =
                storyboard.instantiateInitialViewController()
        }

Ici, nous fournissons l’argument onUpdateComplete à updateTranslations en tant que fermeture d’échappement. Cette fonction de rappel sera appelée lorsqu’il y aura de nouvelles traductions récupérées avec succès. Lorsque cela se produit, nous appelons notre méthode personnalisée AppDelegate.reloadViewController(). Cela rechargera effectivement notre application et montrera à l’utilisateur nos traductions les plus récentes.

✋🏽 Attention » Soyez vigilant lors du rechargement du contrôleur de vue racine de l’application. Si l’utilisateur a déjà commencé à faire quelque chose dans votre application, il se peut que son flux soit interrompu (et que ses données non enregistrées soient perdues) lorsque le rechargement se produit. Pour réduire la probabilité que cela se produise, vous voudrez peut-être utiliser une configuration de timeout relativement basse lors de la configuration du SDK PhraseApp. De cette façon, si la demande de traduction prend du temps, elle s’annulera simplement et ne déclenchera pas le rechargement. Alternativement, et de façon plus fiable, vous pourriez faire apparaître un indicateur de chargement bloquant pendant la requête. Cela empêcherait l’utilisateur d’interagir avec votre application pendant le chargement de la traduction, évitant ainsi la perte de données mentionnée ci-dessus.

La liberté des traductions iOS over-the-air avec Phrase

Nous avons pratiquement terminé d’intégrer les traductions OTA dans notre application. Après avoir publié cette version de notre application sur l’App Store, nous sommes libres d’envoyer des traductions à nos utilisateurs sans passer à nouveau par l’approbation de l’App Store. Il nous suffit de nous rendre sur notre console web Phrase, et :

  1. Mettre à jour nos traductions des paramètres régionaux, puis
  2. Créer une nouvelle version pour notre distribution OTA (tout comme nous l’avons fait auparavant), et
  3. Publier la version.

Publication de la version | Phrase

N’oubliez pas de publier votre version après l’avoir vérifiée

C’est aussi simple que ça. Une fois que nous publions notre version dans la console web Phrase, nos utilisateurs commenceront à recevoir les traductions mises à jour. Tous les utilisateurs qui ont la version de l’application connectée au SDK PhraseApp récupéreront les nouvelles traductions over-the-air. Pas besoin de publier une nouvelle version de l’application, pas besoin d’attendre l’approbation de l’App Store. Ça, c’est la liberté !

🔗 Ressources » Vous pouvez voir et télécharger le code terminé pour le projet iOS que nous avons construit ici depuis notre dépôt Github.

Pour en savoir plus

Si vous souhaitez en savoir plus sur l’internationalisation et la localisation iOS, nous vous suggérons de consulter les articles suivants sur le sujet (en anglais) :

Voilà, c’est terminé !

Connecter notre application aux traductions over-the-air de Phrase ne prend qu’un jour ou deux. Cela peut nous faire économiser des dizaines et des dizaines d’heures, en diffusant nos nouvelles traductions directement aux utilisateurs de nos applications. Avec OTA, nous n’avons pas besoin de l’approbation de l’App Store (en cas de simple mise à jour du texte). Phrase vous offre plus que la technologie OTA : notre plateforme peut vraiment rationaliser le processus de localisation pour votre application. Découvrez tous les produits Phrase et inscrivez-vous pour un essai gratuit de 14 jours.
J’espère que vous commencez à entrevoir tout le potentiel des traductions over-the-air et que vous avez apprécié ce petit guide pour utiliser OTA dans vos applications iOS. Nous ajouterons plus de contenu OTA dans les jours à venir, alors restez à l’écoute, et bonne programmation :)

Posts associés

Blog post

Localiser les jeux Unity avec le plug-in officiel Phrase

Vous voulez localiser votre jeu Unity sans avoir à jongler avec les fichiers .csv ? Découvrez comment le plug-in officiel Phrase Strings pour Unity simplifie le flux de travaux de localisation de votre jeu, de la configuration du tableau de chaînes à l’importation directe des traductions dans votre projet. Que vous développiez pour l’allemand, le serbe ou d’autres langues, ce guide vous explique comment démarrer rapidement et localiser comme un professionnel.

Software localization blog category featured image | Phrase

Blog post

Guide ultime de la localisation JavaScript

Démarrez la localisation JavaScript de votre navigateur avec ce guide complet et préparez votre application pour les utilisateurs internationaux.

Software localization blog category featured image | Phrase

Blog post

Traduire du contenu Flutter en over-the-air avec Phrase

Arrêtez d’attendre l’approbation du magasin d’applications mobiles. Avec Phrase Over the Air, générez du nouveau contenu pour les utilisateurs de vos applications Flutter instantanément à travers le monde.

Software localization blog category featured image | Phrase

Blog post

Guide ultime de la localisation Flutter

Décodons ensemble les secrets de la localisation Flutter afin que vous permettre de parler la langue de vos utilisateurs et de continuer à coder votre chemin vers le succès à l’international.

Software localization blog category featured image | Phrase

Blog post

Comment utiliser le plug-in de localisation de Phrase Strings pour WordPress

Apprenez à traduire les pages WordPress ou encore des articles dans plusieurs langues avec l’intégration Phrase Strings pour WordPress.