{"id":133320,"date":"2019-06-19T09:00:26","date_gmt":"2019-06-19T07:00:26","guid":{"rendered":"https:\/\/phrase.com\/blog\/posts\/traduccion-de-la-app-de-ios-de-forma-inalambrica-con-phrase-strings\/"},"modified":"2026-02-26T16:50:51","modified_gmt":"2026-02-26T15:50:51","slug":"ios-over-the-air-translation-with-phrase","status":"publish","type":"post","link":"https:\/\/phrase.com\/es\/blog\/posts\/ios-over-the-air-translation-with-phrase\/","title":{"rendered":"Traducci\u00f3n Over-the-air de apps en iOS con Phrase Strings"},"content":{"rendered":"<p>A la hora de implementar nuevas traducciones en nuestra aplicaci\u00f3n, nuestro equipo no necesita depender de nosotros, los desarrolladores, ni ocupar nuestro valioso tiempo. Tambi\u00e9n podemos ahorrarnos el proceso de aprobaci\u00f3n de la App Store al actualizar el contenido localizado de nuestra aplicaci\u00f3n. Veamos c\u00f3mo podemos aprovechar estos beneficios en nuestra app de iOS con la funci\u00f3n de traducci\u00f3n Over-the-air en iOS de Phrase. Agregaremos traducci\u00f3n OTA en IOS con Phrase a una aplicaci\u00f3n en funcionamiento, conectando todo en la consola de Phrase y nuestro proyecto de XCode.<\/p>\n<div id=\"ez-toc-container\" class=\"ez-toc-v2_0_81 counter-hierarchy ez-toc-counter ez-toc-grey ez-toc-container-direction\">\n<div class=\"ez-toc-title-container\">\n<p class=\"ez-toc-title\" style=\"cursor:inherit\">Overview<\/p>\n<span class=\"ez-toc-title-toggle\"><a href=\"#\" class=\"ez-toc-pull-right ez-toc-btn ez-toc-btn-xs ez-toc-btn-default ez-toc-toggle\" aria-label=\"Alternar tabla de contenidos\"><span class=\"ez-toc-js-icon-con\"><span class=\"\"><span class=\"eztoc-hide\" style=\"display:none;\">Toggle<\/span><span class=\"ez-toc-icon-toggle-span\"><svg style=\"fill: #999;color:#999\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" class=\"list-377408\" width=\"20px\" height=\"20px\" viewBox=\"0 0 24 24\" fill=\"none\"><path d=\"M6 6H4v2h2V6zm14 0H8v2h12V6zM4 11h2v2H4v-2zm16 0H8v2h12v-2zM4 16h2v2H4v-2zm16 0H8v2h12v-2z\" fill=\"currentColor\"><\/path><\/svg><svg style=\"fill: #999;color:#999\" class=\"arrow-unsorted-368013\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"10px\" height=\"10px\" viewBox=\"0 0 24 24\" version=\"1.2\" baseProfile=\"tiny\"><path d=\"M18.2 9.3l-6.2-6.3-6.2 6.3c-.2.2-.3.4-.3.7s.1.5.3.7c.2.2.4.3.7.3h11c.3 0 .5-.1.7-.3.2-.2.3-.5.3-.7s-.1-.5-.3-.7zM5.8 14.7l6.2 6.3 6.2-6.3c.2-.2.3-.5.3-.7s-.1-.5-.3-.7c-.2-.2-.4-.3-.7-.3h-11c-.3 0-.5.1-.7.3-.2.2-.3.5-.3.7s.1.5.3.7z\"\/><\/svg><\/span><\/span><\/span><\/a><\/span><\/div>\n<nav><ul class='ez-toc-list ez-toc-list-level-1 ' ><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-1\" href=\"https:\/\/phrase.com\/es\/blog\/posts\/ios-over-the-air-translation-with-phrase\/#nuestra-aplicacion\" >Nuestra aplicaci\u00f3n<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-2\" href=\"https:\/\/phrase.com\/es\/blog\/posts\/ios-over-the-air-translation-with-phrase\/#preparacion\" >Preparaci\u00f3n<\/a><ul class='ez-toc-list-level-3' ><li class='ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-3\" href=\"https:\/\/phrase.com\/es\/blog\/posts\/ios-over-the-air-translation-with-phrase\/#como-mover-las-traducciones-de-nuestra-aplicacion-de-storyboards-a-localizablestrings\" >C\u00f3mo mover las traducciones de nuestra aplicaci\u00f3n de Storyboards a Localizable.strings<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-4\" href=\"https:\/\/phrase.com\/es\/blog\/posts\/ios-over-the-air-translation-with-phrase\/#como-anadir-una-distribucion-de-traduccion-over-the-air-en-ios-en-phrase\" >C\u00f3mo a\u00f1adir una distribuci\u00f3n de traducci\u00f3n Over-the-air en iOS en Phrase<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-5\" href=\"https:\/\/phrase.com\/es\/blog\/posts\/ios-over-the-air-translation-with-phrase\/#agrega-tu-primera-version-ota\" >Agrega tu primera versi\u00f3n OTA<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-6\" href=\"https:\/\/phrase.com\/es\/blog\/posts\/ios-over-the-air-translation-with-phrase\/#instalando-el-sdk-de-phrase-para-ios\" >Instalando el SDK de Phrase para iOS<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-7\" href=\"https:\/\/phrase.com\/es\/blog\/posts\/ios-over-the-air-translation-with-phrase\/#configurando-phrase-en-nuestra-aplicacion-ios\" >Configurando Phrase en nuestra aplicaci\u00f3n iOS<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-8\" href=\"https:\/\/phrase.com\/es\/blog\/posts\/ios-over-the-air-translation-with-phrase\/#actualizar-manualmente-la-interfaz-de-usuario-despues-de-importar-nuevas-traducciones\" >Actualizar manualmente la interfaz de usuario despu\u00e9s de importar nuevas traducciones<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-9\" href=\"https:\/\/phrase.com\/es\/blog\/posts\/ios-over-the-air-translation-with-phrase\/#la-libertad-de-la-traduccion-over-the-air-en-ios-con-phrase\" >La libertad de la traducci\u00f3n Over-the-air en iOS con Phrase<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-10\" href=\"https:\/\/phrase.com\/es\/blog\/posts\/ios-over-the-air-translation-with-phrase\/#mas-informacion\" >M\u00e1s informaci\u00f3n<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-11\" href=\"https:\/\/phrase.com\/es\/blog\/posts\/ios-over-the-air-translation-with-phrase\/#%c2%a1eso-es-todo\" >\u00a1Eso es todo!<\/a><\/li><\/ul><\/nav><\/div>\n<h2 id=\"toc_1\"><span class=\"ez-toc-section\" id=\"nuestra-aplicacion\"><\/span>Nuestra aplicaci\u00f3n<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Continuamos donde lo dejamos en nuestro art\u00edculo anterior, <a href=\"https:\/\/phrase.com\/blog\/posts\/ios-app-localization-phrase\/\">iOS App Localization with Phrase<\/a>. En ese tutorial, tomamos una aplicaci\u00f3n y la conectamos a Phrase para optimizar nuestro flujo de trabajo de localizaci\u00f3n. Asumimos que has le\u00eddo ese art\u00edculo o que ya tienes Phrase conectado a tu app. Si no, te recomendamos mucho que revises ese art\u00edculo antes de continuar.<\/p>\n<p style=\"text-align: center\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-6831 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2019\/06\/otaios01-sim-en-app-two-screens.png\" alt=\"Aplicaci\u00f3n de demostraci\u00f3n | Phrase\" width=\"832\" height=\"749\" \/><em>Short Circuit, nuestra aplicaci\u00f3n de demostraci\u00f3n, una lista seleccionada de m\u00fasica electr\u00f3nica<\/em><\/p>\n<p>Nuestra peque\u00f1a aplicaci\u00f3n de demostraci\u00f3n es una lista de canciones de m\u00fasica electr\u00f3nica junto con sus artistas y fechas de lanzamiento. Es una aplicaci\u00f3n sencilla de dos pantallas con un <code>UITableViewController<\/code> que muestra todas nuestras pistas y tiene una pantalla de detalles para mostrar la informaci\u00f3n de cada una.<\/p>\n<blockquote><p>\ud83d\udd17 <em>Recurso \u00bb<\/em> Si quieres seguir el proceso, puedes descargar el <a href=\"https:\/\/github.com\/PhraseApp-Blog\/ios-ota-phraseapp-starter\">proyecto de inicio<\/a> de Github. Tienes un enlace al proyecto completo en la parte inferior del art\u00edculo.<\/p><\/blockquote>\n<h2 id=\"toc_2\"><span class=\"ez-toc-section\" id=\"preparacion\"><\/span>Preparaci\u00f3n<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Vale, vamos a preparar nuestra app para la traducci\u00f3n Over-the-air en iOS. En nuestra aplicaci\u00f3n iOS, moveremos nuestro texto localizado de los storyboards a nuestras conocidas y fiables <code>Localizable.strings<\/code>. Luego agregaremos soporte para traducci\u00f3n OTA en iOS a nuestro proyecto de Phrase. Para completar nuestra preparaci\u00f3n, instalaremos el SDK de Phrase para iOS, que sincroniza las traducciones de nuestra aplicaci\u00f3n con nuestro proyecto de traducci\u00f3n Over-the-air en iOS con Phrase.<\/p>\n<h3 id=\"toc_3\"><span class=\"ez-toc-section\" id=\"como-mover-las-traducciones-de-nuestra-aplicacion-de-storyboards-a-localizablestrings\"><\/span>C\u00f3mo mover las traducciones de nuestra aplicaci\u00f3n de Storyboards a Localizable.strings<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>La traducci\u00f3n Over-the-air en iOS con Phrase funcionar\u00e1 perfectamente con los archivos <code>.strings<\/code> de storyboard. Sin embargo, para no complicar demasiado esta explicaci\u00f3n, pongamos todas nuestras cadenas que se pueden traducir en un solo archivo <code>Localizable.strings<\/code>. Es una refactorizaci\u00f3n simple de <code>Main.strings<\/code> a <code>Localizable.strings<\/code>, as\u00ed que manos a la obra.<\/p>\n<blockquote><p>\ud83d\udcd6 <em>Profundiza m\u00e1s \u00bb<\/em> Si quieres saber m\u00e1s sobre la localizaci\u00f3n de storyboard en iOS, consulta nuestra gu\u00eda de <a href=\"https:\/\/phrase.com\/blog\/posts\/ios-i18n-internationalizing-storyboards-in-xcode\/\">internacionalizaci\u00f3n de iOS: Internacionalizaci\u00f3n de Storyboards en Xcode<\/a><\/p><\/blockquote>\n<p>Comencemos con los t\u00edtulos de los elementos de navegaci\u00f3n de nuestra pantalla. Podemos simplemente sobrescribir los t\u00edtulos establecidos en nuestros storyboards configurando el campo <code>title<\/code> en nuestros controladores de vista. Nuestra pantalla de inicio, con un listado de nuestros artistas, est\u00e1 conectada a <code>TrackListViewController<\/code>. Actualicemos ese controlador ahora.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"swift\" data-enlighter-group=\"1\" data-enlighter-title=\"TrackListViewController.swift\">class TrackListViewController: UIViewController {\n   \/\/ ...\n    override func viewDidLoad() {\n        super.viewDidLoad()\n        title = NSLocalizedString(\"trackListTitle\", comment: \"\")\n        \/\/ ...\n    }\n    \/\/ ...\n}<\/pre>\n<p>Usamos el habitual <code>NSLocalizedString<\/code> para obtener nuestra traducci\u00f3n de <code>Localizable.strings<\/code>. Vamos a asegurarnos de que la cadena de traducci\u00f3n exista en la versi\u00f3n en ingl\u00e9s de <code>Localizable.strings<\/code>.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"swift\" data-enlighter-group=\"2\" data-enlighter-title=\"Localizable.strings\">\"copyright\" = \"Copyright \u00a9 %@ %@. All rights reserved\";\n\"trackListTitle\" = \"Short Circuit\";<\/pre>\n<p>Con esto, nuestra aplicaci\u00f3n se ver\u00e1 exactamente igual cuando la ejecutes en ingl\u00e9s. Agregaremos nuestros idiomas que no son en ingl\u00e9s un poco m\u00e1s tarde. Primero, terminemos de mover nuestras traducciones fuera de nuestros storyboards.<br \/>\nPara nuestras etiquetas, tenemos que asegurarnos de que tengamos conexiones desde nuestros storyboards a sus respectivos controladores.<\/p>\n<p>Despu\u00e9s de conectar nuestras etiquetas, simplemente repetimos el mismo proceso de llamar a <code>NSLocalizedString<\/code> para proporcionarles cadenas traducidas. Y, por supuesto, agregamos nuestras nuevas cadenas a nuestro archivo <code>Localizable.strings<\/code> en ingl\u00e9s.<br \/>\nNuestro <code>TrackDetailsViewController<\/code>, que ayuda a mostrar la informaci\u00f3n de una sola pista, se ver\u00eda as\u00ed despu\u00e9s de mover nuestras traducciones de storyboard:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"swift\" data-enlighter-group=\"3\" data-enlighter-title=\"TrackDetailsViewController.swift\" data-enlighter-linenumbers=\"false\">import UIKit\nclass TrackDetailsViewController: UIViewController {\n    @IBOutlet weak var trackNameHeaderLabel: UILabel!\n    @IBOutlet weak var artistNameHeaderLabel: UILabel!\n    \/\/ ...\n    var track: Track?\n    override func viewDidLoad() {\n        super.viewDidLoad()\n        title = NSLocalizedString(\"trackDetailsTitle\", comment: \"\")\n        if let track = track {\n            trackNameHeaderLabel.text =\n                getLocalizedHeaderText(key: \"trackNameHeader\")\n            artistNameHeaderLabel.text =\n                getLocalizedHeaderText(key: \"artistNameHeader\")\n            releaseDateHeaderLabel.text =\n                getLocalizedHeaderText(key: \"releaseDateHeader\")\n            \/\/ ...\n        }\n    }\n    \/\/ ...\n    fileprivate func getLocalizedHeaderText(key: String) -&gt; String {\n        return NSLocalizedString(key, comment: \"\")\n            .localizedUppercase\n    }\n}<\/pre>\n<p>La funci\u00f3n <code>getLocalizedHeaderText<\/code> es solo una peque\u00f1a funci\u00f3n auxiliar que usamos para proporcionar un formato en may\u00fasculas adaptado al idioma a nuestro texto de etiqueta traducido.<br \/>\nDespu\u00e9s de agregar nuestras cadenas de traducci\u00f3n a nuestras <code>Localizable.strings<\/code>, nuestra aplicaci\u00f3n tiene exactamente el mismo aspecto que antes.<\/p>\n<p style=\"text-align: center\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-6833 size-large\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2019\/06\/otaios01-after-storyboard-labels-moved-607x1024.png\" alt=\"Aplicaci\u00f3n con refactorizaci\u00f3n exitosa | Phrase\" width=\"607\" height=\"1024\" \/><em>Parece que la refactorizaci\u00f3n ha funcionado<\/em><\/p>\n<p>Ahora podemos eliminar completamente nuestros archivos de traducci\u00f3n de los storyboards de nuestro proyecto de Xcode. Lo hacemos seleccionando el archivo de storyboard en el <em>navegador de proyectos<\/em> y desmarcando cada localizaci\u00f3n bajo el encabezado de <em>localizaciones<\/em> en el <em>inspector de archivos<\/em>.<\/p>\n<blockquote><p>\ud83d\udcd6 <em>Profundiza m\u00e1s \u00bb<\/em> Si trabajas mucho con storyboards de iOS, puede que te interese nuestro art\u00edculo sobre <a href=\"https:\/\/phrase.com\/blog\/posts\/ios-automate-ios-storyboard-localization\/\">automatizar la localizaci\u00f3n de storyboards de iOS<\/a>.<\/p><\/blockquote>\n<h4 id=\"toc_4\">Actualizando nuestras traducciones con Phrase<\/h4>\n<p>Ahora necesitamos actualizar las traducciones de <code>Localizable.strings<\/code> para los idiomas que no son el idioma fuente (que no sean el ingl\u00e9s, en mi caso). Hacemos esto para que los usuarios que no hablan ingl\u00e9s puedan ver nuestras etiquetas de actualizaci\u00f3n en su idioma. Podemos, por supuesto, usar nuestro flujo de trabajo habitual de Phrase para traducir nuestras etiquetas.<br \/>\nPrimero, subiremos nuestro nuevo archivo fuente haciendo un <code>$ phrase push<\/code> desde la l\u00ednea de comandos.<br \/>\nNuestras nuevas claves de traducci\u00f3n ya deber\u00edan aparecer en la consola web de Phrase. Una vez que nuestros traductores hayan traducido las claves a todos los idiomas que admite nuestra aplicaci\u00f3n, podemos hacer <code>$ phrase pull<\/code> desde la l\u00ednea de comandos para obtener nuestros archivos <code>Localizable.strings<\/code> actualizados. Ahora, cuando ejecutamos nuestra aplicaci\u00f3n en un idioma que no es el original (\u00e1rabe en mi caso), vemos que nuestras vistas est\u00e1n traducidas como se esperaba.<\/p>\n<p style=\"text-align: center\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-6835 size-large\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2019\/06\/otaios01-after-phraseapp-pull-600x1024.png\" alt=\"Aplicaci\u00f3n de demostraci\u00f3n traducida | Phrase\" width=\"600\" height=\"1024\" \/><em>Todo bien en todos los idiomas<\/em><\/p>\n<blockquote><p>\ud83d\udcd6 <em>Profundiza m\u00e1s \u00bb<\/em> Puedes aprender m\u00e1s sobre el flujo de trabajo de traducci\u00f3n de Phrase con iOS en nuestro art\u00edculo sobre <a href=\"https:\/\/phrase.com\/blog\/posts\/ios-app-localization-phrase\/\">localizaci\u00f3n de aplicaciones iOS con Phrase<\/a>.<\/p><\/blockquote>\n<p>Ahora que hemos movido nuestras cadenas de traducci\u00f3n de storyboard a archivos <code>Localizable.strings<\/code>, podemos pasar a configurar traducciones OTA en iOS a nuestro proyecto de Phrase.<\/p>\n<h3 id=\"toc_5\"><span class=\"ez-toc-section\" id=\"como-anadir-una-distribucion-de-traduccion-over-the-air-en-ios-en-phrase\"><\/span>C\u00f3mo a\u00f1adir una distribuci\u00f3n de traducci\u00f3n Over-the-air en iOS en Phrase<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Vamos a dejar nuestro proyecto de Phrase listo para la traducci\u00f3n Over-the-air en iOS. En nuestra consola web de Phrase, vamos a abrir el men\u00fa desplegable de nuestra organizaci\u00f3n en la barra de navegaci\u00f3n superior y seleccionar <em>Integraciones<\/em>.<\/p>\n<p style=\"text-align: center\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-6836 size-large\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2019\/06\/otaios01-integrations-menu-item-1024x628.png\" alt=\"Abriendo el men\u00fa de Integraciones en Phrase | Phrase \" width=\"1024\" height=\"628\" \/><em>OTA se encuentra en<\/em> Integraciones<\/p>\n<p>Esto abrir\u00e1 nuestra p\u00e1gina de <a href=\"https:\/\/phrase.com\/es\/integrations\/\"><em>Integraciones de Phrase<\/em><\/a>. Aqu\u00ed podemos buscar la fila <em>Over-the-Air (OTA)<\/em> y hacer clic en su bot\u00f3n de <em>configuraci\u00f3n<\/em>.<\/p>\n<p style=\"text-align: center\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-6837 size-large\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2019\/06\/otaios01-integrations-page-1024x741.png\" alt=\"Configurando OTA | Phrase\" width=\"1024\" height=\"741\" \/><em>El creciente n\u00famero de integraciones de Phrase<\/em><\/p>\n<p>Ahora deber\u00edamos ver la p\u00e1gina <em>Over-the-air<\/em>, que nos invita a crear una distribuci\u00f3n. Una distribuci\u00f3n es una configuraci\u00f3n OTA de un proyecto que tiene como objetivo una o m\u00e1s plataformas y cuenta con opciones de respaldo.<\/p>\n<p style=\"text-align: center\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-6838 size-large\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2019\/06\/otaios01-ota-page-1024x434.png\" alt=\"Creando una distribuci\u00f3n para la aplicaci\u00f3n de demostraci\u00f3n | Phrase\" width=\"1024\" height=\"434\" \/><em>Podemos crear una distribuci\u00f3n OTA espec\u00edfica para nuestra aplicaci\u00f3n iOS<\/em><\/p>\n<p>Haz clic en el bot\u00f3n <em>Crear distribuci\u00f3n<\/em> para abrir el di\u00e1logo <em>Agregar distribuci\u00f3n<\/em>.<\/p>\n<p style=\"text-align: center\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-6839 size-large\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2019\/06\/otaios01-add-distribtution-dialog-853x1024.png\" alt=\"Opciones de distribuci\u00f3n de la aplicaci\u00f3n | Phrase\" width=\"853\" height=\"1024\" \/><em>Las<\/em> opciones para a\u00f1adir distribuci\u00f3n <em>son bastante autoexplicativas<\/em><\/p>\n<p>Para nuestro proyecto actual, podemos darle a la distribuci\u00f3n un nombre que elijamos, seleccionar el proyecto de Phrase asociado con nuestra app de iOS y marcar la casilla de la plataforma <em>iOS<\/em>. Podemos dejar las opciones de respaldo predeterminadas tal como est\u00e1n, ya que se ajustan a nuestras necesidades. Haz clic en el bot\u00f3n <em>Save<\/em> para crear la distribuci\u00f3n.<\/p>\n<blockquote><p>\ud83d\udca1<em>Nota \u00bb<\/em> Puedes cambiar la configuraci\u00f3n de tu distribuci\u00f3n en cualquier momento volviendo a la p\u00e1gina <em>Over-the-air<\/em>.<\/p><\/blockquote>\n<h3 id=\"toc_6\"><span class=\"ez-toc-section\" id=\"agrega-tu-primera-version-ota\"><\/span>Agrega tu primera versi\u00f3n OTA<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Para trabajar con el SDK de iOS sin ver un error cuando intentamos actualizar nuestras traducciones en la aplicaci\u00f3n, necesitamos un lanzamiento de OTA. Una versi\u00f3n es b\u00e1sicamente una instant\u00e1nea de nuestras traducciones de Phrase que podemos probar y desplegar en nuestra aplicaci\u00f3n de traducci\u00f3n Over-the-air en iOS. M\u00e1s adelante, hablaremos de los lanzamientos. Por ahora, crea una primera versi\u00f3n para empezar a trabajar con el SDK.<br \/>\nEmpezaremos haciendo clic en el bot\u00f3n <em>Crear lanzamiento<\/em> en la parte inferior de nuestra p\u00e1gina de distribuci\u00f3n.<\/p>\n<p style=\"text-align: center\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-6841 size-large\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2019\/06\/otaios01-create-release-button-1024x621.png\" alt=\"Bot\u00f3n de crear lanzamiento | Phrase\" width=\"1024\" height=\"621\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2019\/06\/otaios01-create-release-button-1024x621.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2019\/06\/otaios01-create-release-button-300x182.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2019\/06\/otaios01-create-release-button-768x465.png 768w, https:\/\/phrase.com\/wp-content\/uploads\/2019\/06\/otaios01-create-release-button.png 1170w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><br \/>\n<em>L\u00e1nzame<\/em><\/p>\n<p>Esto abre el di\u00e1logo para <em>a\u00f1adir el lanzamiento<\/em>. Podemos a\u00f1adir una descripci\u00f3n para ayudarnos a identificar el lanzamiento m\u00e1s tarde; por ahora deja todo lo dem\u00e1s como est\u00e1 y haz clic en <em>Guardar<\/em>.<\/p>\n<p style=\"text-align: center\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-6840 size-large\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2019\/06\/otaios01-first-release-887x1024.png\" alt=\"Men\u00fa de a\u00f1adir lanzamiento | Phrase\" width=\"887\" height=\"1024\" \/><em>Un lanzamiento es una instant\u00e1nea de traducci\u00f3n que podemos desplegar<\/em><\/p>\n<p>Ya deber\u00edas ver una entrada en la secci\u00f3n de <em>lanzamientos<\/em> cerca de la parte inferior de nuestra p\u00e1gina de distribuci\u00f3n.<br \/>\nAhora que tenemos una distribuci\u00f3n OTA y un primer lanzamiento, podemos configurar OTA en nuestra aplicaci\u00f3n de iOS.<\/p>\n<h3 id=\"toc_7\"><span class=\"ez-toc-section\" id=\"instalando-el-sdk-de-phrase-para-ios\"><\/span>Instalando el SDK de Phrase para iOS<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Necesitar\u00e1s tener el SDK de Phrase instalado para usar OTA. Podemos hacerlo a trav\u00e9s de CocoaPods, <a href=\"https:\/\/github.com\/Carthage\/Carthage\">Carthage<\/a>, o manualmente.<br \/>\nPara instalar el SDK a trav\u00e9s de CocoaPods, necesitamos tener CocoaPods instalado en nuestro Mac. Puedes ver las instrucciones de instalaci\u00f3n en el sitio web de CocoaPods. Suponiendo que tenemos CocoaPods instalado, podemos navegar a nuestro directorio ra\u00edz del proyecto (el que contiene el archivo <code>.xcodeproj<\/code>) y ejecutar el siguiente comando en la terminal.<\/p>\n<div>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\">$ pod init<\/pre>\n<\/div>\n<p>Esto crear\u00e1 un <code>Podfile<\/code> en nuestro directorio ra\u00edz. Abramos este archivo en nuestro editor de c\u00f3digo favorito y agreguemos una l\u00ednea para que se vea as\u00ed:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"ruby\" data-enlighter-linenumbers=\"false\">target 'phraseapp-demo' do\n  use_frameworks!\n  # Pods for phraseapp-demo\n  pod 'PhraseSDK'\nend<\/pre>\n<p>Con esa edici\u00f3n guardada, podemos ejecutar lo siguiente desde nuestra terminal:<\/p>\n<div>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\" data-enlighter-linenumbers=\"false\">$ pod install<\/pre>\n<\/div>\n<p>Si todo sali\u00f3 bien, deber\u00edamos haber recibido el mensaje de que se ha instalado Phrase. Despu\u00e9s de eso, podemos cerrar cualquier ventana de XCode que tengamos abierta y abrir el archivo <code>.xcworkspace<\/code> que CocoaPods agreg\u00f3 a la ra\u00edz de nuestro proyecto despu\u00e9s de instalar Phrase.<\/p>\n<h3 id=\"toc_8\"><span class=\"ez-toc-section\" id=\"configurando-phrase-en-nuestra-aplicacion-ios\"><\/span>Configurando Phrase en nuestra aplicaci\u00f3n iOS<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Con el SDK instalado, vamos a conectarlo a nuestra app. Primero, echemos un vistazo r\u00e1pido a dos m\u00e9todos principales que expone el SDK de Phrase.<br \/>\n<code>Phrase.shared.setup(distributionID:environmentSecret:timeout:)<\/code> inicializa el objeto singleton compartido de Phrase, utilizando nuestras credenciales y un par\u00e1metro de tiempo de espera opcional.<br \/>\n<code>Phrase.shared.updateTranslations(translationResult:)<\/code> actualiza manualmente las traducciones en la aplicaci\u00f3n, obteniendo nuevas desde los servidores de Phrase si existe un nuevo lanzamiento disponible. <code>updateTranslations<\/code> toma un cierre de escape opcional, <code>translationResult<\/code>, que podemos proporcionar si queremos actuar cuando se complete la solicitud de actualizaci\u00f3n.<\/p>\n<p>Ok, agreguemos una clase <code>OTATranslations<\/code> que ofrece una capa ligera alrededor de <code>PhraseApp<\/code>.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"swift\" data-enlighter-group=\"4\" data-enlighter-title=\"OTATranslations.swift\" data-enlighter-linenumbers=\"false\">import PhraseSDK\nclass OTATranslations {\n    static let shared = OTATranslations()\n    private init() {\n        #if DEBUG\n        Phrase.shared.debugMode = true\n        #endif\n        let config: PList? = loadConfig()\n        if let config = config {\n            #if DEBUG\n            let environmentTokenKey: String = \"devToken\"\n            #else\n            let environmentTokenKey: String = \"prodToken\"\n            #endif\n            Phrase.shared.setup(\n                distributionID: config.getValue(withKey: \"distributionID\"),\n                environmentSecret: config.getValue(withKey: environmentTokenKey),\n                timeout: config.getValue(withKey: \"timeout\")\n            )\n        }\n    }\n    func updateTranslations(onUpdateComplete: (() -&gt; Void)? = nil) {\n        do {\n            try Phrase.shared.updateTranslations { result in\n                switch result {\n                case .success(let updated):\n                    if updated {\n                        printIfDebug(\"Translations updated successfully\")\n                        onUpdateComplete?()\n                    } else {\n                        printIfDebug(\"No new translations found\")\n                    }\n                case .failure:\n                    printIfDebug(\"Failure updating translations\")\n                }\n            }\n        } catch {\n            printIfDebug(\"Error updating translations: \\(error)\")\n        }\n    }\n    fileprivate func loadConfig() -&gt; PList? {\n        do {\n            return try PList(pListResource: \"PhraseApp\")\n        } catch {\n            printIfDebug(\n                \"Error loading PhraseApp configuration from plist, \\(error)\")\n        }\n        return nil\n    }\n}<\/pre>\n<blockquote><p>\ud83d\udca1<em>Dato \u00bb<\/em> <code>Phrase.shared.debugMode<\/code> es un indicador que hace que el SDK de Phrase sea m\u00e1s expl\u00edcito, mostrando m\u00e1s detalles en la consola cuando est\u00e1 activado. Lo activamos cuando estamos ejecutando una compilaci\u00f3n de depuraci\u00f3n de nuestra aplicaci\u00f3n.<br \/>\n\ud83d\udca1\u00a0<em>FYI \u00bb<\/em> La funci\u00f3n <code>printIfDebug(_:)<\/code> es un ayudante personalizado que simplemente muestra en la consola la cadena que se le pasa si el indicador <code>#DEBUG<\/code> est\u00e1 activado.<\/p><\/blockquote>\n<p>Proporcionamos un objeto singleton simple, <code>OTATranslations.shared<\/code>, para usar en nuestra aplicaci\u00f3n. Nuestro objeto se inicializa extrayendo valores de configuraci\u00f3n de Phrase de una <code>PhraseApp.plist<\/code> y proporcion\u00e1ndolos a <code>PhraseApp.setup<\/code>. Para que esto funcione, necesitamos crear la clase <code>PhraseApp.plist<\/code> en nuestro proyecto de XCode. Hag\u00e1moslo ahora. Deber\u00eda tener un aspecto similar al siguiente.<\/p>\n<p style=\"text-align: center\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-6842 size-large\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2019\/06\/otaios01-phraseapp-plist-keys-values-1024x173.png\" alt=\"Claves | Phrase\" width=\"1024\" height=\"173\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2019\/06\/otaios01-phraseapp-plist-keys-values-1024x173.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2019\/06\/otaios01-phraseapp-plist-keys-values-300x51.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2019\/06\/otaios01-phraseapp-plist-keys-values-768x129.png 768w, https:\/\/phrase.com\/wp-content\/uploads\/2019\/06\/otaios01-phraseapp-plist-keys-values.png 1424w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><em>Aseg\u00farate de que tus claves coincidan con las de aqu\u00ed<\/em><\/p>\n<p>Aqu\u00ed hay una versi\u00f3n de texto si quieres <code>\u2318-C<\/code>.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"xml\" data-enlighter-group=\"5\" data-enlighter-title=\"PhraseApp.plist\" data-enlighter-linenumbers=\"false\">&lt;?xml version=\"1.0\" encoding=\"UTF-8\"?&gt;\n&lt;!DOCTYPE plist PUBLIC \"-\/\/Apple\/\/DTD PLIST 1.0\/\/EN\" \"http:\/\/www.apple.com\/DTDs\/PropertyList-1.0.dtd\"&gt;\n&lt;plist version=\"1.0\"&gt;\n&lt;dict&gt;\n    &lt;key&gt;distributionID&lt;\/key&gt;\n    &lt;string&gt;your_distribution_id&lt;\/string&gt;\n    &lt;key&gt;devToken&lt;\/key&gt;\n    &lt;string&gt;your_distribution_development_secret&lt;\/string&gt;\n    &lt;key&gt;prodToken&lt;\/key&gt;\n    &lt;string&gt;your_distribution_production_secret&lt;\/string&gt;\n    &lt;key&gt;timeout&lt;\/key&gt;\n    &lt;integer&gt;10&lt;\/integer&gt;\n&lt;\/dict&gt;\n&lt;\/plist&gt;<\/pre>\n<blockquote><p>\u270b\ud83c\udffd <em>Atenci\u00f3n \u00bb<\/em> Quiz\u00e1 quieras agregar el archivo <code>PhraseApp.plist<\/code> a tu <code>.gitignore<\/code> para proteger tus claves secretas.<br \/>\n\ud83d\udca1<em>Para tu informaci\u00f3n \u00bb<\/em> La clase <code>PList<\/code> que usamos para cargar los valores de nuestro archivo <code>.plist<\/code> es una clase auxiliar personalizada.<\/p><\/blockquote>\n<p>Los valores de nuestro <code>distributionID<\/code>, <code>devToken<\/code> y <code>prodToken<\/code> se pueden encontrar en la p\u00e1gina de distribuci\u00f3n OTA en la consola web de Phrase. Podemos navegar a la pesta\u00f1a de <em>Resumen<\/em> del tablero de control de nuestro proyecto y hacer clic en el bot\u00f3n <em>Ver distribuciones<\/em> en la secci\u00f3n <em>Over-the-air<\/em>. Esto abre la p\u00e1gina <em>Over-the-air<\/em>, y desde all\u00ed podemos encontrar y hacer clic en la distribuci\u00f3n OTA de nuestro proyecto en la lista <em>Distribuciones<\/em>. Una vez en la p\u00e1gina de la distribuci\u00f3n espec\u00edfica, podemos encontrar los valores de <em>ID de distribuci\u00f3n<\/em>, <em>Secreto de desarrollo<\/em> y <em>Secreto de producci\u00f3n<\/em> para copiarlos y pegarlos en nuestro archivo <code>.plist<\/code>. Por supuesto, estamos usando \u00abtoken\u00bb en lugar de \u00absecret\u00bb en nuestra <code>.plist<\/code>, pero probablemente ya te habr\u00e1s dado cuenta.<\/p>\n<p style=\"text-align: center\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-6843 size-large\" src=\"https:\/\/phraseapp.com\/blog\/content\/uploads\/2019\/06\/otaios01-distribution-id-and-keys-on-console-1024x602.png\" alt=\"Copiar ID y tokens secretos | Phrase\" width=\"1024\" height=\"602\" \/><em style=\"font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen-Sans, Ubuntu, Cantarell, 'Helvetica Neue', sans-serif\">Copiar y pegar tu ID y tus tokens secretos<\/em><\/p>\n<p>Ok, ahora que tenemos el SDK de Phrase conectado a nuestra clase <code>OTATranslations<\/code>, hagamos uso de \u00e9l en nuestro <code>AppDelegate.swift<\/code>.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"swift\" data-enlighter-group=\"6\" data-enlighter-title=\"AppDelegate.swift\" data-enlighter-linenumbers=\"false\">import UIKit\n@UIApplicationMain\nclass AppDelegate: UIResponder, UIApplicationDelegate {\n    var window: UIWindow?\n    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -&gt; Bool {\n        OTATranslations.shared.updateTranslations()\n        return true\n    }\n}<\/pre>\n<p>En el m\u00e9todo <code>AppDelegate<\/code> de <code>application(_:didFinishLaunchingWithOptions:)<\/code>, que se ejecuta cuando nuestra aplicaci\u00f3n se inicia por primera vez, actualizamos manualmente nuestras traducciones de forma remota. Esto incorpora nuevas traducciones en segundo plano. Sin embargo, nuestra configuraci\u00f3n actual no har\u00e1 que la interfaz de usuario se actualice de inmediato. Las actualizaciones de traducci\u00f3n que se obtienen durante este lanzamiento de la aplicaci\u00f3n aparecer\u00e1n para el usuario durante su <em>pr\u00f3ximo<\/em> lanzamiento de la aplicaci\u00f3n. Este es a menudo el comportamiento que queremos: que nuevas cadenas de traducci\u00f3n aparezcan de repente mientras el usuario est\u00e1 en medio de hacer algo en la aplicaci\u00f3n podr\u00eda ser una experiencia de usuario extra\u00f1a y potencialmente inquietante.<\/p>\n<h3 id=\"toc_9\"><span class=\"ez-toc-section\" id=\"actualizar-manualmente-la-interfaz-de-usuario-despues-de-importar-nuevas-traducciones\"><\/span>Actualizar manualmente la interfaz de usuario despu\u00e9s de importar nuevas traducciones<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Sin embargo, dependiendo de nuestras necesidades, puede que necesitemos actualizar la interfaz de usuario de la app inmediatamente despu\u00e9s de recibir una actualizaci\u00f3n de traducci\u00f3n. Podemos hacer eso utilizando el par\u00e1metro de cierre de callback que proporcionamos en <code>updateTranslations(onUpdateComplete:)<\/code>.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"swift\" data-enlighter-group=\"7\" data-enlighter-title=\"AppDelegate.swift\" data-enlighter-linenumbers=\"false\">import UIKit\n@UIApplicationMain\nclass AppDelegate: UIResponder, UIApplicationDelegate {\n    var window: UIWindow?\n    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -&gt; Bool {\n        OTATranslations.shared.updateTranslations() {\n            AppDelegate.reloadRootViewController()\n        }\n        return true\n    }\n    static func reloadRootViewController() {\n        DispatchQueue.main.async {\n            let storyboard = UIStoryboard.init(name: \"Main\", bundle: nil)\n            let appDelegate = UIApplication.shared.delegate as! AppDelegate\n            appDelegate.window?.rootViewController =\n                storyboard.instantiateInitialViewController()\n        }<\/pre>\n<p>Aqu\u00ed proporcionamos el argumento <code>onUpdateComplete<\/code> a <code>updateTranslations<\/code> como un cierre de escape. Este closure se llamar\u00e1 cuando haya nuevas traducciones que se hayan obtenido con \u00e9xito. Cuando esto sucede, llamamos a nuestro m\u00e9todo personalizado <code>AppDelegate.reloadViewController()<\/code>. Esto recargar\u00e1 nuestra aplicaci\u00f3n y mostrar\u00e1 al usuario nuestras traducciones m\u00e1s recientes.<\/p>\n<blockquote><p>\u270b\ud83c\udffd <em>Aviso \u00bb<\/em> Ten cuidado al recargar el controlador de vista ra\u00edz de la aplicaci\u00f3n. Si el usuario ya ha comenzado a hacer algo en tu aplicaci\u00f3n, puede que ese flujo se interrumpa\u00a0\u2013\u00a0y sus datos no guardados se pierdan\u00a0\u2013\u00a0cuando ocurra la recarga. Para reducir las posibilidades de que esto suceda, es posible que desees usar una configuraci\u00f3n de <code>timeout<\/code> relativamente baja al configurar el SDK de <code>PhraseApp<\/code>. De esa manera, si la solicitud de traducci\u00f3n tarda un tiempo, simplemente se cancelar\u00e1 y no activar\u00e1 la recarga. Alternativamente, y de forma m\u00e1s robusta, puedes hacer que aparezca un indicador de carga bloqueante durante la solicitud. Esto impedir\u00eda que el usuario interact\u00fae por completo con tu aplicaci\u00f3n durante la carga de traducci\u00f3n, evitando la p\u00e9rdida de datos mencionada anteriormente.<\/p><\/blockquote>\n<h2 id=\"toc_10\"><span class=\"ez-toc-section\" id=\"la-libertad-de-la-traduccion-over-the-air-en-ios-con-phrase\"><\/span>La libertad de la traducci\u00f3n Over-the-air en iOS con Phrase<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Ya casi hemos terminado de integrar las traducciones OTA en nuestra aplicaci\u00f3n. Despu\u00e9s de publicar <em>esta<\/em> versi\u00f3n de nuestra aplicaci\u00f3n en la App Store, podemos enviar traducciones a nuestros usuarios sin pasar por la aprobaci\u00f3n de la App Store de nuevo. Todo lo que tenemos que hacer es ir a nuestra consola web de Phrase y<\/p>\n<ol>\n<li>Actualiza nuestras traducciones de localizaci\u00f3n y luego<\/li>\n<li>crea un nuevo lanzamiento para nuestra distribuci\u00f3n de OTA (igual que hicimos antes), y<\/li>\n<li>publica el lanzamiento.<\/li>\n<\/ol>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-6854 size-large\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2019\/06\/otaios01-publish-release-1024x54.png\" alt=\"Publicando el lanzamiento | Phrase\" width=\"1024\" height=\"54\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2019\/06\/otaios01-publish-release-1024x54.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2019\/06\/otaios01-publish-release-300x16.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2019\/06\/otaios01-publish-release-768x41.png 768w, https:\/\/phrase.com\/wp-content\/uploads\/2019\/06\/otaios01-publish-release.png 1170w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/p>\n<p style=\"text-align: center\"><em>No olvides publicar tu lanzamiento despu\u00e9s de revisarlo<\/em><\/p>\n<p>De verdad que es as\u00ed de simple. Una vez que publiquemos nuestra versi\u00f3n en la consola web de Phrase, nuestros usuarios comenzar\u00e1n a recibir las traducciones actualizadas. Todos los usuarios que tengan la versi\u00f3n de la aplicaci\u00f3n conectada al SDK de <code>PhraseApp<\/code> descargar\u00e1n las nuevas traducciones Over-the-air. No necesitas publicar una nueva versi\u00f3n de tu aplicaci\u00f3n, ni esperar la aprobaci\u00f3n del App Store. Dulce libertad.<\/p>\n<blockquote><p>\ud83d\udd17 <em>Recurso \u00bb<\/em> Puedes ver y descargar el c\u00f3digo completo del proyecto de iOS que construimos aqu\u00ed desde <a href=\"https:\/\/github.com\/PhraseApp-Blog\/ios-ota-phraseapp-complete\">nuestro repositorio de GitHub<\/a>.<\/p><\/blockquote>\n<h2 id=\"toc_11\"><span class=\"ez-toc-section\" id=\"mas-informacion\"><\/span>M\u00e1s informaci\u00f3n<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Si quieres profundizar en la internacionalizaci\u00f3n y localizaci\u00f3n de iOS, echa un vistazo a algunos de nuestros otros art\u00edculos sobre este tema.<\/p>\n<blockquote>\n<ul>\n<li><a href=\"https:\/\/phrase.com\/blog\/posts\/ios-tutorial-internationalization-localization\/\">Quick Guide: iOS Internationalization (i18n) and Localization (l10n) <\/a><\/li>\n<li><a href=\"https:\/\/phrase.com\/blog\/posts\/internationalizing-a-full-stack-ios-app-firebase-part-1-user-interface\/\">Internationalizing a Full-Stack iOS App with Firebase (Part 1): The User Interface <\/a><\/li>\n<li><a href=\"https:\/\/phrase.com\/blog\/posts\/internationalizing-a-full-stack-ios-app-firebase-part-2-firebase-i18n\/\">Internationalizing a Full-Stack iOS App with Firebase (Part 2): Firebase i18n<\/a><\/li>\n<\/ul>\n<\/blockquote>\n<h2 id=\"toc_12\"><span class=\"ez-toc-section\" id=\"%c2%a1eso-es-todo\"><\/span>\u00a1Eso es todo!<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Solo tardar\u00e1s un par de d\u00edas en conectar nuestra aplicaci\u00f3n a las traducciones Over-the-air de Phrase. Esto puede ahorrarnos decenas de horas mientras publicamos nuevas traducciones directamente a nuestros usuarios de la aplicaci\u00f3n. Con OTA tambi\u00e9n tenemos la ventaja a\u00f1adida de saltarnos la aprobaci\u00f3n de la App Store (solo para una actualizaci\u00f3n de texto). Y Phrase te ofrece mucho m\u00e1s que OTA: Phrase puede realmente agilizar el proceso de localizaci\u00f3n de tu aplicaci\u00f3n. Echa un vistazo a todos los <a href=\"https:\/\/phrase.com\/es\/platform\/\"><span style=\"font-weight: 400\">productos de Phrase<\/span><\/a>\u00a0y <a href=\"https:\/\/eu.phrase.com\/idm-ui\/signup\">reg\u00edstrate<\/a> para una prueba gratuita de 14 d\u00edas.<br \/>\nEspero que est\u00e9s comenzando a ver el potencial de las traducciones OTA, y que hayas disfrutado de esta peque\u00f1a gu\u00eda sobre c\u00f3mo hacer que OTA funcione en tu aplicaci\u00f3n iOS. Estaremos a\u00f1adiendo m\u00e1s contenido OTA en los pr\u00f3ximos d\u00edas, as\u00ed que mantente atento, \u00a1y feliz programaci\u00f3n! :)<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Si quieres asegurarte de que todos los usuarios de tu aplicaci\u00f3n obtengan el texto correcto, la funci\u00f3n de traducci\u00f3n Over-the-Air de Phrase puede ayudarte a publicar tus actualizaciones de traducci\u00f3n al instante.<\/p>\n","protected":false},"author":41,"featured_media":2612,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"post-refresh-updated","format":"standard","meta":{"_acf_changed":false,"_stopmodifiedupdate":false,"_modified_date":"","_searchwp_excluded":"","episode_type":"","audio_file":"","podmotor_file_id":"","podmotor_episode_id":"","cover_image":"","cover_image_id":"","duration":"","filesize":"","filesize_raw":"","date_recorded":"","explicit":"","block":"","itunes_episode_number":"","itunes_title":"","itunes_season_number":"","itunes_episode_type":"","footnotes":""},"categories":[2520],"class_list":["post-133320","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-localizacion-de-software-es"],"acf":[],"_links":{"self":[{"href":"https:\/\/phrase.com\/es\/wp-json\/wp\/v2\/posts\/133320","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/phrase.com\/es\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/phrase.com\/es\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/phrase.com\/es\/wp-json\/wp\/v2\/users\/41"}],"replies":[{"embeddable":true,"href":"https:\/\/phrase.com\/es\/wp-json\/wp\/v2\/comments?post=133320"}],"version-history":[{"count":4,"href":"https:\/\/phrase.com\/es\/wp-json\/wp\/v2\/posts\/133320\/revisions"}],"predecessor-version":[{"id":136301,"href":"https:\/\/phrase.com\/es\/wp-json\/wp\/v2\/posts\/133320\/revisions\/136301"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/phrase.com\/es\/wp-json\/wp\/v2\/media\/2612"}],"wp:attachment":[{"href":"https:\/\/phrase.com\/es\/wp-json\/wp\/v2\/media?parent=133320"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/phrase.com\/es\/wp-json\/wp\/v2\/categories?post=133320"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}