objective c - localizable - Las mejores prácticas con NSLocalizedString
localization xcode 9 (9)
En Swift estoy usando lo siguiente, por ejemplo, para el botón "Sí" en este caso:
NSLocalizedString("btn_yes", value: "Yes", comment: "Yes button")
Tenga en cuenta el uso del value:
para el valor de texto predeterminado. El primer parámetro sirve como ID de traducción. La ventaja de usar el parámetro value:
es que el texto predeterminado puede cambiarse más tarde pero la ID de traducción permanece igual. El archivo Localizable.strings contendrá "btn_yes" = "Yes";
Si el parámetro value:
no se usó, el primer parámetro se usará para ambos: para la ID de traducción y también para el valor de texto predeterminado. El archivo Localizable.strings contendría "Yes" = "Yes";
. Este tipo de administración de archivos de localización parece ser extraño. Especialmente si el texto traducido es largo, la identificación también es larga. Cada vez que se cambia cualquier carácter del valor de texto predeterminado, también se cambia la ID de traducción. Esto genera problemas cuando se utilizan sistemas de traducción externos. El cambio de la ID de traducción se entiende como la adición de un nuevo texto de traducción, que puede no ser siempre deseado.
Estoy (como todos los demás) usando NSLocalizedString
para localizar mi aplicación.
Desafortunadamente, hay varios "inconvenientes" (no necesariamente el error de NSLocalizedString mismo), incluidos
- No hay autocompletición de cadenas en Xcode. Esto hace que trabajar no solo sea propenso a errores sino también tedioso.
- Puede terminar redefiniendo una cadena simplemente porque no sabía que una cadena equivalente ya existía (es decir, "Por favor, introduzca la contraseña" frente a "Introduzca la contraseña primero")
- De forma similar al problema de autocompletado, debe "recordar" / copiar las cadenas de comentarios, de lo contrario,
genstring
terminará con varios comentarios para una cadena. - Si desea usar
genstring
después de haber localizado algunas cadenas, debe tener cuidado de no perder las localizaciones anteriores. - Las mismas cadenas están dispersas a lo largo de todo su proyecto. Por ejemplo, utilizó
NSLocalizedString(@"Abort", @"Cancel action")
todas partes, y luego Code Review le pide cambiar el nombre de la cadena aNSLocalizedString(@"Cancel", @"Cancel action")
para hacer el código más consistente .
Lo que hago (y después de algunas búsquedas en SO, me figuré que muchas personas hacen esto) es tener un archivo separado strings.h
donde strings.h
todo el código de localización. Por ejemplo
// In strings.h
#define NSLS_COMMON_CANCEL NSLocalizedString(@"Cancel", nil)
// Somewhere else
NSLog(@"%@", NSLS_COMMON_CANCEL);
Básicamente, esto proporciona la finalización del código, un lugar único para cambiar los nombres de las variables (por lo que ya no es necesario usar genstring) y una palabra clave única para refactorizar automáticamente. Sin embargo, esto tiene el costo de terminar con un montón de sentencias #define
que no están intrínsecamente estructuradas (es decir, como LocString.Common.Cancel o algo así).
Entonces, aunque esto funciona bastante bien, me preguntaba cómo lo harían ustedes en sus proyectos. ¿Existen otros enfoques para simplificar el uso de NSLocalizedString? ¿Hay tal vez incluso un marco que lo encapsula?
En cuanto a la autocompletición de cadenas en Xcode, puedes probar http://questbe.at/lin/ .
Escribí un script para ayudar a mantener Localizable.strings en varios idiomas. Si bien no ayuda en el autocompletado, ayuda a fusionar archivos .strings mediante el comando:
merge_strings.rb ja.lproj/Localizable.strings en.lproj/Localizable.strings
Para obtener más información, consulte: https://github.com/hiroshi/merge_strings
Algunos de ustedes lo encuentran útil, espero.
Estoy de acuerdo con ndfred, pero me gustaría agregar esto:
El segundo parámetro se puede usar como ... valor predeterminado !!
(NSLocalizedStringWithDefaultValue no funciona correctamente con genstring, por eso propuse esta solución)
Aquí está mi implementación personalizada que usa NSLocalizedString que usa comentario como valor predeterminado:
1. En su encabezado precompilado (archivo .pch), redefina la macro ''NSLocalizedString'':
// cutom NSLocalizedString that use macro comment as default value
#import "LocalizationHandlerUtil.h"
#undef NSLocalizedString
#define NSLocalizedString(key,_comment) [[LocalizationHandlerUtil singleton] localizedString:key comment:_comment]
2. crea una clase para implementar el controlador de localización
#import "LocalizationHandlerUtil.h"
@implementation LocalizationHandlerUtil
static LocalizationHandlerUtil * singleton = nil;
+ (LocalizationHandlerUtil *)singleton
{
return singleton;
}
__attribute__((constructor))
static void staticInit_singleton()
{
singleton = [[LocalizationHandlerUtil alloc] init];
}
- (NSString *)localizedString:(NSString *)key comment:(NSString *)comment
{
// default localized string loading
NSString * localizedString = [[NSBundle mainBundle] localizedStringForKey:key value:key table:nil];
// if (value == key) and comment is not nil -> returns comment
if([localizedString isEqualToString:key] && comment !=nil)
return comment;
return localizedString;
}
@end
3. ¡Úselo!
Asegúrese de agregar una secuencia de comandos Ejecutar en las Fases de compilación de la aplicación para que el archivo Localizable.strings se actualice en cada compilación, es decir, se agregará una nueva cadena localizada en su archivo Localized.strings:
Mi script de fase de compilación es un script de shell:
Shell: /bin/sh
Shell script content: find . -name /*.m | xargs genstrings -o MyClassesFolder
Entonces cuando agregas esta nueva línea en tu código:
self.title = NSLocalizedString(@"view_settings_title", @"Settings");
Luego realice una compilación, su archivo ./Localizable.scripts contendrá esta nueva línea:
/* Settings */
"view_settings_title" = "view_settings_title";
Y dado que la clave == value para ''view_settings_title'', el CustomizedStringHandler personalizado devolverá el comentario, es decir, ''Configuración''
Voilà :-)
Si alguien está buscando una solución Swift. Es posible que desee verificar mi solución que reuní aquí: SwiftyLocalization
Con unos pocos pasos para la configuración, tendrá una localización muy flexible en la hoja de cálculo de Google (comentario, color personalizado, resaltado, fuente, varias hojas y más).
En resumen, los pasos son: Google Spreadsheet -> CSV files -> Localizable.strings
Además, también genera Localizables.swift, una estructura que actúa como interfaces para la recuperación y decodificación de claves para usted (sin embargo, debe especificar manualmente una forma de decodificar Cadena desde la clave).
¿Por qué es esto genial?
- Ya no necesita tener una clave como cadena simple en todos los lugares.
- Las claves incorrectas se detectan en tiempo de compilación.
- Xcode puede hacer autocompletar.
Si bien hay herramientas que pueden autocompletar tu clave localizable. La referencia a una variable real asegurará que siempre sea una clave válida, de lo contrario no se compilará.
// It''s defined as computed static var, so it''s up-to-date every time you call.
// You can also have your custom retrieval method there.
button.setTitle(Localizables.login.button_title_login, forState: .Normal)
El proyecto utiliza Google App Script para convertir hojas de cálculo -> CSV y Python para convertir archivos CSV -> Localizable.strings Puede echar un vistazo rápido a esta hoja de ejemplo para saber qué es posible.
Yo mismo, a menudo me dejo llevar por la codificación, olvidándome de poner las entradas en los archivos .strings. Por lo tanto, tengo scripts de ayuda para encontrar lo que debo devolver a los archivos .strings y traducir.
Como utilizo mi propia macro sobre NSLocalizedString, revise y actualice la secuencia de comandos antes de usar, ya que asumí por simplicidad que nil se usa como segundo parámetro de NSLocalizedString. La parte que te gustaría cambiar es
NSLocalizedString/(@(".*?")/s*,/s*nil/)
Simplemente reemplácelo con algo que coincida con su macro y el uso de NSLocalizedString.
Aquí viene el guión, solo necesitas la Parte 3 de hecho. El resto es ver más fácil de dónde viene todo:
// Part 1. Get keys from one of the Localizable.strings
perl -ne ''print "$1/n" if /^/s*(".+")/s*=/'' myapp/fr.lproj/Localizable.strings
// Part 2. Get keys from the source code
grep -n -h -Eo -r ''NSLocalizedString/(@(".*?")/s*,/s*nil/)'' ./ | perl -ne ''print "$1/n" if /NSLocalizedString/(@(".+")/s*,/s*nil/)/''
// Part 3. Get Part 1 and 2 together.
comm -2 -3 <(grep -n -h -Eo -r ''NSLocalizedString/(@(".*?")/s*,/s*nil/)'' ./ | perl -ne ''print "$1/n" if /NSLocalizedString/(@(".+")/s*,/s*nil/)/'' | sort | uniq) <(perl -ne ''print "$1/n" if /^/s*(".+")/s*=/'' myapp/fr.lproj/Localizable.strings | sort) | uniq >> fr-localization-delta.txt
El archivo de salida contiene claves que se encontraron en el código, pero no en el archivo Localizable.strings. Aquí hay una muestra:
"MPH"
"Map Direction"
"Max duration of a detailed recording, hours"
"Moving ..."
"My Track"
"New Trip"
Ciertamente se puede pulir más, pero pensé que lo compartiría.
con iOS 7 y Xcode 5, debe evitar usar el método ''Localization.strings'' y usar el nuevo método ''base de localización''. Hay algunos tutoriales si busca en google ''localización base''
Apple doc: localización de base
NSLocalizedString
tiene algunas limitaciones, pero es tan importante para Cocoa que no es razonable escribir código personalizado para manejar la localización, lo que significa que tendrá que usarlo. Dicho esto, una pequeña herramienta puede ayudar, así es como procedo:
Actualizando el archivo de cadenas
genstrings
sobrescribe sus archivos de cadena, descartando todas sus traducciones anteriores. Escribí update_strings.py para analizar el antiguo archivo de cadenas, ejecutar genstrings
y rellenar los espacios en blanco para que no tenga que restaurar manualmente sus traducciones existentes. El script intenta hacer coincidir los archivos de cadena existentes lo más cerca posible para evitar tener un diff demasiado grande al actualizarlos.
Nombrando tus cuerdas
Si usa NSLocalizedString
como se anuncia:
NSLocalizedString(@"Cancel or continue?", @"Cancel notice message when a download takes too long to proceed");
Puede terminar definiendo la misma cadena en otra parte de su código, lo cual puede entrar en conflicto ya que el mismo término en inglés puede tener diferentes significados en diferentes contextos ( OK
y Cancel
vienen a la mente). Es por eso que siempre uso una cadena sin mayúsculas sin sentido con un prefijo específico del módulo y una descripción muy precisa:
NSLocalizedString(@"DOWNLOAD_CANCEL_OR_CONTINUE", @"Cancel notice window title when a download takes too long to proceed");
Usando la misma cuerda en diferentes lugares
Si usa la misma cadena varias veces, puede usar una macro como lo hizo o almacenarla en caché como una variable de instancia en su controlador de vista o su fuente de datos. De esta forma, no tendrá que repetir la descripción que puede volverse obsoleta e incoherente entre instancias de la misma localización, lo que siempre es confuso. Como las variables de instancia son símbolos, podrá usar la autocompletación en estas traducciones más comunes y usar cadenas "manuales" para las específicas, que solo ocurrirían una vez.
¡Espero que seas más productivo con la localización de Cocoa con estos consejos!
#define PBLocalizedString(key, val) /
[[NSBundle mainBundle] localizedStringForKey:(key) value:(val) table:nil]