Para consolidar los mensajes repetidos en una app para Mac, asigna a cada significado compartido una clave de localización y una definición de Swift, y luego úsala en todos los lugares donde aparezca ese mensaje. Mantén juntos sus argumentos, las reglas de pluralización y el contexto para los traductores; que el texto en inglés coincida no basta.
Esta guía está dirigida a un desarrollador de Mac que mantiene un resumen de selección tanto en SwiftUI como en una ventana de Interface Builder. La tarea concreta es mostrar mensajes como 2 elementos seleccionados en Lote A. de forma coherente, sin mantener plantillas de frases separadas en un XIB y en el código Swift. Todas las cantidades y etiquetas que aparecen a continuación son sintéticas.
Decide qué mensajes repetidos deben agruparse
Antes de editar los catálogos, define el contrato del mensaje: qué significa la frase, qué valores acepta y dónde aparece. Para este ejemplo, el contrato es:
| Elemento del contrato | Definición |
|---|---|
| Significado | Número de elementos seleccionados actualmente dentro de una colección con nombre |
| Clave | selection.summary |
| Tabla | Localizable |
| Paquete | Paquete principal de la app |
| Argumento 1 | Un recuento de selección Int64 |
| Argumento 2 | Una etiqueta literal de colección, como Lote A |
| Variación | Forma plural determinada por el recuento de selección |
| Contexto | Una frase completa de estado que se muestra en SwiftUI y en una etiqueta de AppKit |
Usa este contrato para comprobar si los mensajes deben consolidarse. Dos apariciones pueden compartir el mensaje cuando coinciden en significado, argumentos y función gramatical. El recuento de elementos seleccionados y el recuento total de elementos deben mantenerse separados, aunque ambos muestren 2 elementos en este momento. El botón llamado Seleccionar también necesita su propio mensaje, porque expresa una acción.
Apple admite claves explícitas con valores predeterminados separados precisamente para desambiguar el texto. Así puedes nombrar un mensaje según su propósito y conservar un texto fuente legible. Un comentario aporta contexto; la clave identifica la entrada. Inicializador de String con clave explícita.
Considera también la tabla y el paquete como parte del contrato de búsqueda. Una clave de otra tabla constituye un destino de búsqueda distinto. Apple documenta la selección de tablas mediante el parámetro table o tableName y recomienda pasar texto localizable a través de LocalizedStringResource. Para este ejemplo, mantén el helper y el catálogo en el destino de la app y usa el paquete principal. Preparar el texto de tu app para la traducción.
Comprende la diferencia entre la extracción y el formato en tiempo de ejecución
La extracción ocurre durante la compilación de Xcode: detecta expresiones localizables compatibles y sincroniza su texto fuente con los catálogos. La localización en tiempo de ejecución ocurre cuando la app resuelve un mensaje y sustituye valores reales. Encontrar una entrada en un catálogo demuestra que la extracción la detectó; no demuestra que todos los controles la muestren correctamente.
Interface Builder tiene su propio flujo de trabajo para recursos. Apple documenta catálogos asociados a un storyboard o XIB, mientras que el código Swift normalmente se añade a la tabla Localizable. El flujo de trabajo documentado no describe un alias entre recursos que combine mensajes solo porque coincida el texto visible. Una opción práctica y compatible es asignar el mensaje localizado compartido desde Swift después de cargar la interfaz. Esta implementación se basa en las API documentadas por Apple. Descubrir los catálogos de cadenas.
SwiftUI también distingue entre una clave de localización literal y un valor de cadena normal. Por ejemplo, Text("Summary") solicita la localización, mientras que pasar una variable String muestra su valor sin realizar esa búsqueda. Envolver una cadena en tiempo de ejecución en LocalizedStringKey solicita la búsqueda, pero no debe confundirse con demostrar que la extracción detectó sus posibles valores. LocalizedStringKey.
Por tanto, el diseño siguiente mantiene el literal extraíble dentro de un único helper. Este lleva el recuento y la etiqueta en un LocalizedStringResource y luego resuelve ese recurso donde se necesita una cadena completa. Apple describe este tipo de recurso como una forma de aplazar la búsqueda de localización. Está disponible en macOS 13 y versiones posteriores, que es la versión mínima de destino para este ejemplo. LocalizedStringResource.
Define el mensaje compartido una sola vez en Swift
Crea un proyecto de app para macOS con SwiftUI, establece macOS 13 o una versión posterior como destino de implementación y añade Localizable.xcstrings al destino de la app. En la documentación actual de Xcode, la ruta para crearlo es File > New > File from Template; busca string y selecciona String Catalog. Localizar y variar el texto con un catálogo de cadenas.
Reemplaza el punto de entrada generado de la app con el programa completo que aparece a continuación. Elimina el punto de entrada original para que el destino contenga una sola declaración @main. La ventana de SwiftUI funciona de forma independiente de un XIB; la subclase de AppKit incluida se usa para adaptar una etiqueta existente de Interface Builder en la siguiente sección.
import AppKit
import Foundation
import SwiftUI
enum SelectionMessages {
static func summary(
count: Int64,
collectionLabel: String
) -> LocalizedStringResource {
LocalizedStringResource(
"selection.summary",
defaultValue: "\(count) items selected in \(collectionLabel).",
table: "Localizable",
comment: "Complete selection-status sentence used in SwiftUI and an AppKit label. Argument 1 is the selected-item count; argument 2 is a literal collection label. Pluralize by argument 1."
)
}
}
struct SelectionDemoView: View {
private let counts: [Int64] = [0, 1, 2]
private let collectionLabel = "Batch A"
var body: some View {
VStack(alignment: .leading, spacing: 12) {
ForEach(counts, id: \.self) { count in
Text(String(localized: SelectionMessages.summary(
count: count,
collectionLabel: collectionLabel
)))
}
}
.padding(24)
.frame(minWidth: 360, minHeight: 160)
}
}
final class SelectionSummaryField: NSTextField {
override func awakeFromNib() {
super.awakeFromNib()
// Synthetic initial state for the Interface Builder example.
updateSelection(count: 2, collectionLabel: "Batch A")
}
func updateSelection(count: Int64, collectionLabel: String) {
stringValue = String(localized: SelectionMessages.summary(
count: count,
collectionLabel: collectionLabel
))
}
}
@main
struct SelectionDemoApp: App {
var body: some Scene {
WindowGroup {
SelectionDemoView()
}
}
}
La clave estática identifica el mensaje; el valor predeterminado interpolado proporciona la frase fuente y los argumentos en tiempo de ejecución. El inicializador también acepta la tabla, el paquete y el comentario para los traductores. Su paquete predeterminado es el paquete principal. Inicializador de LocalizedStringResource con clave explícita.
La etiqueta Lote A es contenido de ejemplo insertado en la frase. No es otra clave de mensaje. Esta distinción mantiene separadas las etiquetas de las colecciones y el texto que las rodea en la interfaz. Si tu aplicación real usa nombres de colecciones predefinidos y traducidos, localiza esos nombres mediante sus propias definiciones antes de proporcionarlos aquí.
No construyas esta frase a partir de partes localizadas por separado, como un recuento, elementos seleccionados en y una etiqueta. Mantener la frase completa dentro del contrato da a los localizadores una unidad cuyo texto y orden de argumentos se pueden ajustar en conjunto.
Dirige la etiqueta de Interface Builder a través de la misma definición
Para un XIB o storyboard existente, usa una etiqueta NSTextField estática para el resumen. Configura su clase personalizada como SelectionSummaryField y su módulo como el destino de la app. Borra la frase antigua de su valor de texto de diseño, porque esta etiqueta ahora recibe su texto de la definición Swift compartida.
Al cargar la interfaz, la subclase llama a super.awakeFromNib() y asigna el resumen inicial sintético. Apple documenta awakeFromNib() como el punto de enlace posterior a la carga, con las conexiones de salidas y acciones ya establecidas, y exige llamar a la implementación de la superclase. awakeFromNib().
En la ventana real, llama a updateSelection(count:collectionLabel:) en la etiqueta conectada cada vez que cambie la selección, usando los mismos valores del modelo que proporcionas a SwiftUI. Sustituye la asignación inicial sintética por la ruta de inicialización real de la ventana. Una asignación única durante la carga no puede seguir los cambios posteriores de selección.
El adaptador de AppKit establece stringValue, una forma documentada de asignar el texto de un NSTextField. Así, el diseño permanece en Interface Builder y la propiedad del mensaje pasa a Swift. Deja suficiente anchura o permite el ajuste de línea para que quepa la frase completa. NSTextField.
Mantén el texto estático de la interfaz que no esté relacionado con este mensaje en su flujo de trabajo de recursos actual. Esta migración afecta a un mensaje compartido; no hace falta mover todas las etiquetas a Swift. Comprueba también que ningún enlace ni asignación posterior de otro controlador reemplace el resumen con el texto antiguo.
Configura las variantes de plural sin cambiar los argumentos
Elige Product > Build, abre Localizable.xcstrings y busca selection.summary. Haz clic con el botón Control en la entrada, elige Vary by Plural y selecciona el argumento del recuento numérico si Xcode pregunta qué argumento debe variar. Apple documenta este flujo para mensajes interpolados. Localizar y variar el texto con un catálogo de cadenas.
Para la localización fuente en inglés, configura la variante singular para que produzca 1 elemento seleccionado en Lote A. y la otra variante para que produzca frases como 2 elementos seleccionados en Lote A. Conserva ambos marcadores de argumento proporcionados por Xcode. Edita el texto que los rodea en lugar de reemplazar un marcador por un recuento o una etiqueta de ejemplo.
| Entrada sintética | Resultado esperado en inglés tras configurar el plural |
|---|---|
Recuento 0, etiqueta Lote A |
0 elementos seleccionados en Lote A. |
Recuento 1, etiqueta Lote A |
1 elemento seleccionado en Lote A. |
Recuento 2, etiqueta Lote A |
2 elementos seleccionados en Lote A. |
Recuento 2, etiqueta Lote B |
2 elementos seleccionados en Lote B. |
Estos son casos ilustrativos de aceptación, no resultados medidos. Antes de configurar el plural, la frase fuente predeterminada sigue conteniendo items para todos los recuentos. La extracción conserva el mensaje interpolado; no corrige su gramática por sí sola.
El cambio de etiqueta en la última fila es útil para el diagnóstico: si la pantalla sigue mostrando Lote A, es posible que se haya codificado un valor de ejemplo en una variante o que quien llama esté proporcionando un estado desactualizado. Si la etiqueta desaparece, inspecciona los marcadores de posición de la variante antes de cambiar la firma del helper.
Verifica cada ruta de visualización antes de eliminar las entradas antiguas
Prueba por separado la ventana de SwiftUI y la ventana real de Interface Builder. Usa los mismos recuentos y etiquetas en ambas. Comprueba la carga inicial, un cambio posterior de selección y una etiqueta sintética más larga, como Lote A Ampliado. Confirma que el texto coincide y que la frase sigue siendo legible.
Para cada localización admitida, elige Product > Scheme > Edit Scheme, selecciona Run, abre Options y establece App Language. Vuelve a ejecutar la app y revisa ambas rutas de visualización. Apple documenta estas opciones de esquema para probar localizaciones sin cambiar el idioma de todo el sistema. Probar localizaciones al ejecutar la app.
Sigue esta secuencia cuando algo no funcione:
- Falta la entrada: Confirma que el catálogo pertenece al destino de la app y compila el esquema pertinente. Comprueba que
Use Compiler to Extract Swift Stringsesté activado. - El texto fuente aparece de forma inesperada: Comprueba la clave, la tabla, el paquete y la traducción antes de considerar que el texto de respaldo legible es suficiente.
- Solo difiere la etiqueta de Interface Builder: Comprueba su clase personalizada, módulo, ruta de carga y asignaciones posteriores.
- Solo difieren los valores posteriores: Comprueba que ambas interfaces reciban los valores actualizados del modelo.
- El plural es incorrecto: Comprueba qué argumento numérico se seleccionó para la variación y revisa todas las variantes aplicables.
Apple documenta el ajuste de extracción del compilador y explica que las entradas traducidas que ya no se encuentran en el código fuente pueden quedar marcadas como Stale. Ese estado indica que se debe revisar, pero no demuestra que un recurso ya no se use. Revisa la asignación antigua de la interfaz y las llamadas de Swift antes de eliminar una entrada y sus traducciones. Descubrir los catálogos de cadenas.
La consolidación está completa cuando ambas interfaces obtienen el mismo mensaje contextual de una única definición, sus argumentos permanecen intactos y ya no quedan llamadas a las plantillas de frases antiguas. Guarda el contrato y los casos de aceptación junto al helper para que los futuros cambios de texto conserven el significado compartido por ambas ventanas.
Fuentes
Localizar y variar el texto con un catálogo de cadenas — Apple
LocalizedStringKey — Apple
LocalizedStringResource — Apple
init(localized:defaultValue:table:bundle:locale:comment:) — Apple
NSTextField — Apple
awakeFromNib() — Apple