Guides 14 min de lecture

Localiser une application iOS en plus de 40 langues avec les String Catalogs (.xcstrings)

Guide pratique de la localisation d’application iOS avec les catalogues de chaînes de Xcode et les API d’internationalisation Swift : String(localized:), LocalizedStringResource, pluriels, textes d’autorisation, formats, droite à gauche, pseudolangues, fiche App Store et traduction.

Sommaire16 parties

Pour localiser une application iOS en plus de 40 langues, regroupez tous les textes visibles dans des catalogues de chaînes Xcode (Localizable.xcstrings et InfoPlist.xcstrings), laissez Xcode les extraire automatiquement, gérez les pluriels dans le catalogue et les formats avec FormatStyle, testez avec les pseudolangues et les mises en page de droite à gauche, puis traduisez : traduction automatique pour la vitesse, relecture humaine là où elle compte. Terminez par la fiche App Store, qui se localise à part dans App Store Connect. C’est ainsi que Develak publie ses propres apps dans jusqu’à 44 langues.

Points clés

  • Les catalogues de chaînes (.xcstrings, Xcode 15 et ultérieur) remplacent .strings et .stringsdict : un fichier par table, pluriels et variantes par appareil compris.
  • Ne construisez jamais une phrase par concaténation et ne formatez jamais une date ou un nombre à la main.
  • Localisez aussi InfoPlist.xcstrings : les demandes d’autorisation et le nom de l’app font partie de la première impression.
  • Testez avec les pseudolangues avant de payer la moindre traduction.
  • Localisez la fiche App Store (nom, sous-titre, mots-clés, description, captures) pour chaque marché.
  • La traduction automatique, LLM compris, fait un bon premier jet ; faites relire les textes qui vendent, qui facturent ou qui avertissent.

Pourquoi localiser une app iOS ?

L’App Store est disponible dans 175 pays et régions, et chacun y cherche, lit les avis et décide dans sa langue. Une app localisée, avec une fiche localisée, se positionne sur les mots-clés locaux et inspire confiance sur des marchés que la plupart des apps en anglais seul délaissent. iOS permet aussi de choisir la langue de chaque app dans Réglages : chaque langue ajoutée sert donc des utilisateurs qui la veulent explicitement.

Une fois la méthode en place, chaque langue supplémentaire coûte bien moins cher que la première. Nos apps les plus récentes, comme le journal de crises Seizly ou le chronomètre de circuit ApexLap, sont disponibles en 44 langues, dont l’arabe, l’hébreu et l’ourdou (écrits de droite à gauche) et des langues indiennes comme le hindi, le bengali, le tamoul et le télougou ; le pilulier Pillio l’est en 33. Ce n’est tenable que parce que les étapes ci-dessous sont devenues une routine.

Les catalogues de chaînes en cinq minutes

Un catalogue de chaînes (String Catalog) est un fichier JSON à l’extension .xcstrings, apparu avec Xcode 15, qui stocke toutes les chaînes d’une table et toutes leurs traductions. Ajoutez-en un nommé Localizable à votre cible et compilez : Xcode extrait les chaînes des vues SwiftUI, de String(localized:), de LocalizedStringResource et de NSLocalizedString. Chaque entrée affiche un état (New, Needs Review, Translated, Stale) et un commentaire pour les traducteurs. Les fichiers .strings et .stringsdict existants se migrent depuis le menu contextuel du fichier.

// Extrait automatiquement dans Localizable.xcstrings
Text("Take your medication")

// Le commentaire donne au traducteur le contexte nécessaire
Text("Open", comment: "Button: opens the selected bottle's details")

// En dehors des vues SwiftUI
let title = String(localized: "Cellar", comment: "Tab title for the list of bottles")

Les commentaires sont l’amélioration de qualité la moins chère en localisation : « Open » peut être un verbe ou un adjectif, « Archive » un nom ou une action. Xcode 26 sait aussi rédiger des commentaires pour les traducteurs grâce à un modèle exécuté sur l’appareil, et générer des symboles Swift typés pour les chaînes ajoutées à la main au catalogue. Dans un package Swift, définissez defaultLocalization dans Package.swift et cherchez les chaînes dans Bundle.module ; avec Xcode 26, la macro #bundle désigne pour vous le bon bundle, que le code tourne dans une app, un framework ou un package.

Maquette d'un éditeur de catalogue de chaînes : barre latérale des langues avec la progression des traductions, et clés avec leur traduction française, le commentaire pour le traducteur et l'état (Translated, Needs Review, New, Stale), dont une variante de pluriel avec les formes one et other
Illustration : ce que contient un catalogue de chaînes pour chaque clé (commentaire, traduction, état et variantes de pluriel). Maquette stylisée, pas une capture de Xcode.

Les bases de l’i18n en Swift : String(localized:), LocalizedStringResource et formats

L’internationalisation (i18n) est la part du travail qui se joue dans le code : rendre localisable chaque valeur affichée, pour que la localisation (l10n) se résume ensuite à ajouter des traductions. En Swift, une poignée d’API couvrent presque tous les cas, et choisir la bonne décide si une chaîne arrive ou non dans le catalogue.

APIÀ utiliser pourMoment de la rechercheDisponible depuis
Text("…"), Button("…") (LocalizedStringKey)Les chaînes littérales des vues SwiftUIÀ l’affichage de la vueiOS 13
String(localized:)Les textes construits hors des vues : alertes, notifications, view modelsImmédiatement, dans la langue couranteiOS 15
String(localized:defaultValue:)Une clé explicite quand le mot anglais est ambigu (« Call », nom ou verbe)ImmédiatementiOS 15
LocalizedStringResourceLes valeurs résolues plus tard ou dans un autre processus : App Intents, propriétés de modèleDifférée jusqu’à la création d’un String ou d’un TextiOS 16
FormatStyle et .formatted()Dates, nombres, devises, pourcentages, mesures, listesAu formatage, selon la région de l’utilisateuriOS 15
Les principales API de localisation en Swift (disponibilités d’après la documentation d’Apple)
// SwiftUI : un littéral est une LocalizedStringKey, recherchée automatiquement
Text("Settings")

// Une variable String s'affiche telle quelle, sans recherche
let label = "Settings"
Text(label)                      // non localisé
Text(LocalizedStringKey(label))  // recherché ; ajoutez la clé au catalogue vous-même
Text(verbatim: "Pillio")         // jamais localisé

// Hors des vues : résolu immédiatement, l'interpolation devient un placeholder
let toast = String(localized: "Welcome back, \(name)!", comment: "Toast on launch")

// Clé explicite pour un mot source ambigu
let action = String(localized: "call.verb", defaultValue: "Call", comment: "Button: phone the pharmacy")

// Différé : le système le résout plus tard, éventuellement avec d'autres réglages de langue
struct LogDoseIntent: AppIntent {
    static let title: LocalizedStringResource = "Log a dose"
    func perform() async throws -> some IntentResult { .result() }
}

// Le formatage suit la région de l'utilisateur
Text(Date.now, format: .dateTime.day().month(.wide))
Text(0.25, format: .percent)

Le bug d’i18n le plus fréquent en Swift est le deuxième exemple : passer une variable String à Text contourne la localisation sans prévenir, comme l’explique la documentation de LocalizedStringKey. Gardez les littéraux à l’endroit de l’appel, ou stockez des LocalizedStringResource plutôt que des chaînes dans vos modèles. Apple présente LocalizedStringResource comme une référence à une chaîne localisable dont la recherche est différée ; les App Intents s’en servent pour que Siri et Raccourcis puissent résoudre le texte avec leurs propres réglages de langue. Pour savoir dans quelle langue l’app tourne réellement, lisez Bundle.main.preferredLocalizations plutôt que de déduire la langue de l’appareil.

Pluriels et variantes par appareil

Écrivez la phrase une seule fois avec interpolation, puis choisissez Vary by Plural sur cette clé dans le catalogue. Xcode affiche exactement les catégories de pluriel dont chaque langue a besoin :

// Clé du catalogue : "%lld reminders" → Vary by Plural
Text("\(count) reminders")
LanguesCatégories de plurielCe que casse « count == 1 »
Anglais, allemandone, otherRien, c’est pourquoi le bug passe inaperçu
Japonais, chinois, coréen, thaï, vietnamienotherUne forme au singulier inutile apparaît
Russe, ukrainien, polonaisone, few, many, other« 5 » prend la mauvaise terminaison
Arabezero, one, two, few, many, otherQuatre formes sur six sont fausses
Catégories de pluriel (Unicode CLDR) gérées par les catalogues de chaînes

Une chaîne contenant deux nombres (« %lld photos dans %lld albums ») peut varier sur chacun indépendamment. Vary by Device gère les formulations qui dépendent de l’appareil, comme « Touchez pour commencer » sur iPhone et « Cliquez pour commencer » sur Mac.

InfoPlist.xcstrings : demandes d’autorisation et nom de l’app

Ajoutez à la cible un second catalogue nommé InfoPlist : Xcode y place les clés localisables d’Info.plist, du nom affiché sur l’écran d’accueil (CFBundleDisplayName) à chaque texte d’autorisation, de NSCameraUsageDescription et NSMicrophoneUsageDescription à NSHealthShareUsageDescription. Une demande d’autorisation est le moment où vous sollicitez la confiance, et une demande en anglais dans une app en français paraît inachevée précisément à ce moment-là. App Review lit aussi ces textes au titre de la règle 5.1.1 : leurs traductions doivent rester précises.

Deux autres emplacements s’oublient facilement : les phrases des App Shortcuts vivent dans AppShortcuts.xcstrings, et les extensions de widget doivent disposer de leurs chaînes dans la cible de l’extension.

Dates, nombres et unités : laissez faire FormatStyle

L’ordre des dates, l’horloge sur 12 ou 24 heures, le séparateur décimal, la place du symbole monétaire, le premier jour de la semaine et le système de mesure changent tous selon la région de l’utilisateur. Ne codez jamais en dur "MM/dd/yyyy" ni un symbole de devise :

// Jour et heure, dans la langue et le format horaire de l'utilisateur
Text(dose.scheduledAt, format: .dateTime.weekday(.wide).hour().minute())

// Devise avec le bon symbole et les bons séparateurs
Text(price, format: .currency(code: "EUR"))

// Kilomètres ou miles selon la région de l'utilisateur
Text(distance, format: .measurement(width: .abbreviated, usage: .road))

// "Pillio, Seizly, and ApexLap" ou "Pillio, Seizly et ApexLap"
Text(appNames.formatted(.list(type: .and)))

Triez les listes visibles avec localizedStandardCompare et cherchez avec localizedStandardContains, pour que les accents et la casse se comportent comme on s’y attend.

Langues de droite à gauche : arabe, hébreu, ourdou

SwiftUI inverse automatiquement la mise en page quand vous utilisez leading et trailing plutôt que left et right. Les SF Symbols directionnels s’inversent d’eux-mêmes ; pour vos propres images directionnelles, activez l’inversion explicitement. N’inversez pas ce qui n’est pas directionnel : logos, photos, coches. Vérifiez le résultat dans une prévisualisation :

Image("swipe-hint")
    .flipsForRightToLeftLayoutDirection(true)

#Preview("Arabe, droite à gauche") {
    ContentView()
        .environment(\.locale, Locale(identifier: "ar"))
        .environment(\.layoutDirection, .rightToLeft)
}

Tester avant de traduire : les pseudolangues

Dans l’éditeur de schéma (Run › Options › App Language), Xcode propose des pseudolangues qui révèlent les bugs de localisation sans aucune traduction. Activez dans le même panneau « Show non-localized strings » pour repérer le texte codé en dur.

PseudolangueCe qu’elle révèle
Double-LengthLibellés tronqués et boutons rognés, comme avec les longues chaînes allemandes ou finnoises
AccentedDiacritiques coupés, interlignes trop serrés ; les chaînes codées en dur restent sans accents
Bounded StringChaînes concaténées ou tronquées, puisqu’on voit où chacune commence et finit
Right-to-LeftMises en page qui ne s’inversent pas, avec un texte lisible
Right-to-Left with Right-to-Left StringsL’expérience complète de droite à gauche, texte compris
Les pseudolangues de Xcode et les bugs qu’elles révèlent

Les tests d’interface peuvent lancer l’app dans n’importe quelle langue, ce qui permet aussi d’automatiser les captures App Store localisées :

let app = XCUIApplication()
app.launchArguments += ["-AppleLanguages", "(fr)", "-AppleLocale", "fr_FR"]
app.launch()

Localiser aussi la fiche App Store

Le binaire et la fiche se localisent séparément. La ligne « Langues » de votre fiche provient du bundle de l’app ; tout le reste se règle par localisation dans App Store Connect, qui accepte des métadonnées dans 50 langues et variantes régionales :

  • Nom et sous-titre (30 caractères chacun), texte promotionnel, description et nouveautés.
  • Mots-clés (100 octets par localisation : les lettres accentuées et les écritures non latines consomment donc le quota plus vite), à rechercher dans chaque langue plutôt qu’à traduire : un utilisateur français qui cherche un rappel de médicaments peut taper « pilulier », un mot qu’aucune traduction de « pill reminder » ne produit.
  • Captures et aperçus vidéo avec légendes traduites ; à défaut, ceux de la langue principale s’affichent. Les captures en arabe et en hébreu doivent montrer l’interface inversée. Notre guide des tailles de captures App Store détaille les dimensions attendues pour chaque jeu localisé.
  • Noms et descriptions des achats intégrés, et noms des groupes d’abonnements.

Mots-clés et captures localisés relèvent pleinement de l’optimisation App Store : notre service d’optimisation App Store (ASO) traite chaque marché séparément.

Deux localisations de la fiche d'une app fictive côte à côte, anglais (États-Unis) et français, chacune avec son nom, son sous-titre, ses mots-clés, son texte promotionnel, sa description et ses légendes de captures, avec leurs limites en caractères et en octets
Illustration : chaque localisation App Store a ses propres nom, sous-titre, mots-clés, textes et captures ; seule la ligne « Langues » vient du binaire.

Traduction humaine, automatique, ou les deux ?

Les grands modèles de langage traduisent désormais bien les chaînes d’interface quand on leur donne du contexte : le commentaire pour le traducteur, l’écran concerné, un glossaire des termes du produit. Ils trébuchent encore sur le registre (tutoiement ou vouvoiement en français, « du » ou « Sie » en allemand), les accords, la longueur et les placeholders qui doivent rester intacts. Notre règle : traduction automatique pour un premier jet sur tout, puis relecture humaine de l’accueil, du paywall et des conditions d’abonnement, des demandes d’autorisation, des métadonnées App Store et de tout ce qui touche au médical, au juridique ou à la sécurité. Les noms de marque comme Pillio ou Seizly sont marqués « Don’t translate », et les builds TestFlight passent entre les mains de locuteurs natifs avant la publication.

Notre outil : Xlocalize pour les fichiers .xcstrings

Localiser un catalogue de plus de 30 apps nous a conduits à créer notre propre app Mac. Xlocalize ouvre un fichier .xcstrings, vous laisse choisir les langues cibles et traduit avec OpenAI, Anthropic Claude ou Google Gemini, avec votre propre clé d’API stockée dans le trousseau macOS, sans compte à créer. Elle préserve pluriels, variantes par appareil et substitutions, transmet le commentaire de chaque clé comme contexte, suit l’état de chaque clé (nouvelle, traduite, « ne pas traduire ») et signale les placeholders incohérents, les copies non traduites et les traductions manquantes avant qu’ils n’atteignent un build. Elle fonctionne sous macOS 14 ou ultérieur et se télécharge gratuitement, avec des achats intégrés facultatifs. L’étude de cas Xlocalize raconte pourquoi nous l’avons créée.

Checklist du processus de localisation

Schéma du processus en six étapes : internationaliser le code, extraire les chaînes en compilant, tester avec les pseudolangues, traduire avec un premier jet automatique, faire relire par des natifs, puis publier via TestFlight avec une fiche App Store localisée, à recommencer à chaque version pour les chaînes nouvelles et périmées
Illustration : la boucle de localisation que nous suivons pour chaque app, du code internationalisé à la fiche App Store localisée.
  1. Internationalisez le code : aucun texte en dur, FormatStyle partout, mises en page leading et trailing, tailles flexibles et Dynamic Type.
  2. Créez les catalogues Localizable, InfoPlist et, si besoin, AppShortcuts, puis compilez pour extraire les chaînes.
  3. Commentez les chaînes ambiguës et marquez les noms de marque « Don’t translate ».
  4. Configurez les pluriels et les variantes par appareil.
  5. Testez avec les pseudolangues et en droite à gauche.
  6. Traduisez : premier jet automatique, relecture humaine des chaînes critiques.
  7. Vérifiez placeholders et troncatures dans chaque langue, sur le plus petit iPhone pris en charge.
  8. Localisez métadonnées App Store, mots-clés, captures et noms des achats intégrés.
  9. Diffusez la version via TestFlight à des locuteurs natifs.
  10. À chaque version, traduisez les chaînes nouvelles ou périmées avant de soumettre.

Les pièges courants

  • La concaténation. "Hello " + name casse l’ordre des mots ; utilisez une seule chaîne localisée avec interpolation.
  • Une clé, deux sens. Réutiliser « Open » comme verbe et comme adjectif impose une mauvaise traduction dans la moitié des langues.
  • L’ordre des arguments. Le traducteur peut devoir réordonner les valeurs ; les spécificateurs positionnels comme %1$@ et %2$lld le permettent.
  • Le texte dans les images, qu’aucun catalogue ne peut traduire.
  • Les largeurs fixes et les libellés sur une ligne qui débordent en allemand.
  • Les libellés d’accessibilité. VoiceOver lit à voix haute le texte de .accessibilityLabel ; il est extrait comme les autres chaînes, vérifiez qu’il est bien traduit.
  • Les textes côté serveur : notifications push et e-mails se localisent aussi, par exemple avec loc-key dans la charge utile de la notification.
  • Les chaînes périmées restées non traduites après une refonte.

Faire soi-même ou passer par un service de localisation iOS ?

Tout ce qui précède peut se faire en interne avec Xcode et un outil de traduction, et pour deux ou trois langues, c’est généralement la bonne option. Un service de localisation iOS externe devient rentable quand le nombre de langues augmente plus vite que votre capacité à les relire, quand l’app contient des formulations médicales, juridiques ou liées aux abonnements qui ne tolèrent pas l’à-peu-près, ou quand chaque marché demande sa propre recherche de mots-clés plutôt qu’une traduction. Vérifiez que le prestataire travaille directement dans vos fichiers .xcstrings, teste l’app en fonctionnement dans chaque langue plutôt qu’un tableur, et considère la fiche App Store comme faisant partie du travail.

Si vous préférez confier tout le processus à une équipe qui le pratique chaque semaine, notre service de localisation d’application couvre la mise en place des catalogues de chaînes, la traduction et l’adaptation, les vérifications de mise en page et de droite à gauche, la relecture linguistique sur appareil et la fiche App Store localisée.

Questions fréquentes

Qu’est-ce qu’un fichier .xcstrings ?

Un fichier .xcstrings est un catalogue de chaînes Xcode : un fichier JSON, apparu avec Xcode 15, qui stocke toutes les chaînes localisables d’une table avec leurs traductions, formes plurielles, variantes par appareil, commentaires et états de traduction. Il remplace les anciens fichiers .strings et .stringsdict.

Faut-il encore utiliser des fichiers Localizable.strings ?

Pas pour un nouveau projet. Les fichiers Localizable.strings et .stringsdict existants continuent de fonctionner, et Xcode sait les migrer vers un catalogue de chaînes depuis le menu contextuel du fichier. Les catalogues sont un format de compilation : Xcode les transforme en ressources .strings et .stringsdict classiques dans l’app. Il faut donc Xcode 15 ou ultérieur pour compiler, mais la version minimale d’iOS de l’app n’est pas concernée.

Quelle différence entre String(localized:) et LocalizedStringResource ?

String(localized:) (iOS 15 et ultérieur) recherche la traduction immédiatement et renvoie un simple String dans la langue courante. LocalizedStringResource (iOS 16 et ultérieur) ne conserve que la clé, la table, le bundle et la langue, et n’est résolu que plus tard, quand vous en tirez un String ou un Text. Utilisez le premier pour un texte affiché tout de suite, le second pour les valeurs qui circulent : propriétés de modèle, App Intents ou tout ce qui est résolu dans un autre processus.

Par combien de langues commencer ?

Commencez par les langues des marchés que vous voyez déjà dans les statistiques d’App Store Connect, puis par celles que vos concurrents négligent. Une fois le code internationalisé et la méthode en place, ajouter une langue revient surtout à traduire et relire, ce qui permet à un studio comme le nôtre de publier des apps en plus de 40 langues.

Peut-on traduire une app iOS avec ChatGPT, Claude ou Gemini ?

Oui, pour un premier jet. Donnez du contexte au modèle (commentaires, glossaire, écran où apparaît la chaîne), utilisez un outil qui modifie le fichier .xcstrings sans casser pluriels ni placeholders, comme Xlocalize, et faites relire par des locuteurs natifs l’accueil, le paywall, les demandes d’autorisation et les métadonnées App Store.

Comment localiser le nom de mon app ?

Il y a deux noms. Celui qui s’affiche sous l’icône, sur l’écran d’accueil, est CFBundleDisplayName, traduit dans InfoPlist.xcstrings. Celui de l’App Store se règle par localisation dans App Store Connect, dans la limite de 30 caractères. Beaucoup d’apps gardent la marque identique partout et ne localisent que la partie descriptive, comme le sous-titre.

De ce guide à votre app publiée

Develak conçoit, développe et publie des apps iOS natives pour des fondateurs et des équipes du monde entier, en français comme en anglais.

  • Icône de l’app Cedary
  • Icône de l’app Seizly
  • Icône de l’app ApexLap
  • Icône de l’app Bonsaiku
  • Icône de l’app Pillio
  • Icône de l’app SimplyTask
  • Icône de l’app Corkwell
  • Icône de l’app Fosterwell
Les 34 applications
Démarrer un projet