Mejores prácticas usando NSLocalizedString


Estoy (como todos los demás) usando NSLocalizedString para localizar mi aplicación.

Desafortunadamente, hay varios "inconvenientes" (no necesariamente la culpa de NSLocalizedString en sí), incluyendo

  • No hay autocompletición para cadenas en Xcode. Esto hace que trabajar no solo sea propenso a errores, sino también tedioso.
  • Podría terminar redefiniendo una cadena simplemente porque no sabía que ya existía una cadena equivalente (es decir, "Por favor ingrese la contraseña" vs. " Ingrese la contraseña primero")
  • De manera similar al problema de autocompletado, necesita "recordar" / copypaste las cadenas de comentarios, o de lo contrario genstring terminará con múltiples comentarios para una cadena
  • Si desea usar genstring después de haber localizado algunas cadenas, debe tener cuidado de no perder sus localizaciones antiguas.
  • Las mismas cadenas están dispersas a lo largo de todo tu proyecto. Por ejemplo, usó NSLocalizedString(@"Abort", @"Cancel action") en todas partes, y luego Code Review le pide que cambie el nombre de la cadena a NSLocalizedString(@"Cancel", @"Cancel action") para hacer el código más consistente.

Lo que hago (y después de algunas búsquedas, así que me imaginé que muchas personas hacen esto) es tener un archivo strings.h separado donde #define 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);

Esto esencialmente proporciona la finalización del código, un solo lugar para cambiar los nombres de las variables (por lo que ya no es necesario genstring), y una palabra clave única para auto-refactor. Sin embargo, esto viene a costa de terminar con un montón de #define declaraciones que no están inherentemente estructuradas (es decir, como LocString.Común.Cancelar o algo así).

Así que, aunque esto funciona un poco bien, me preguntaba cómo lo hacen ustedes en sus proyectos. Hay otros enfoques para simplificar el uso de NSLocalizedString? ¿Hay tal vez incluso un marco que lo encapsula?

Author: JiaYow, 2012-03-27

9 answers

NSLocalizedString tiene algunas limitaciones, pero es tan central para Cocoa que no es razonable escribir código personalizado para manejar la localización, lo que significa que tendrá que usarlo. Dicho esto, un poco de herramientas puede ayudar, así es como procedo:

Actualizando el archivo strings

genstrings sobrescribe sus archivos de cadena, descartando todas sus traducciones anteriores. Escribí update_strings.py para analizar el archivo de cadenas antiguo, ejecute genstrings y rellene los espacios en blanco para que no tenga que hacerlo manualmente restaura tus traducciones existentes. El script intenta hacer coincidir los archivos de cadena existentes lo más cerca posible para evitar tener una diferencia demasiado grande al actualizarlos.

Nombrando tus cadenas

Si utiliza 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, que puede entrar en conflicto ya que el mismo término en inglés puede tener un significado diferente en diferentes contextos (OK y Cancel vienen a la mente). Es por eso que siempre uso un todo en mayúsculas sin sentido cadena 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 cadena 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 manera usted no tendrá que repetir la descripción que puede conseguir rancio y obtener inconsistente entre las instancias de la misma localización, que siempre es confuso. Como variables de instancia son símbolos, usted será capaz de usar autocompletado en estas traducciones más comunes, y utilizar cadenas "manuales" para las específicas, que solo ocurrirían una vez de todos modos.

Espero que seas más productivo con la localización de cacao con estos consejos!

 89
Author: ndfred,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-10-03 13:30:39

En cuanto a la autocompletición de cadenas en Xcode, puede probar http://questbe.at/lin/.

 30
Author: hiroshi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-09-28 02:22:32

Estoy de acuerdo con ndfred, pero me gustaría añadir 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 pre compilado (.pch), redefine 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. crear una clase para implementar el manejador 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. ¡Úsalo!

Asegúrese de agregar un script de ejecución en las fases de compilación de su aplicación para que pueda ser localizado.el archivo de cadenas se actualizará en cada compilación, es decir, se agregará una nueva cadena localizada en su Localizada.archivo de cadenas:

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

Así que cuando añades esta nueva línea en tu código:

self.title = NSLocalizedString(@"view_settings_title", @"Settings");

A continuación, realizar una construcción, su./ Localizable.el archivo de scripts contendrá este nuevo línea:

/* Settings */
"view_settings_title" = "view_settings_title";

Y desde key = = valor para 'view_settings_title' , el custom LocalizedStringHandler devolverá el comentario, es decir, 'Settings"

Voilà: -)

 22
Author: Pascal,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-05-22 12:04:35

Escribí un script para ayudar a mantener Localizable.cadenas en varios idiomas. Si bien no ayuda en el autocompletado, ayuda a fusionar .cadenas de archivos usando el comando:

merge_strings.rb ja.lproj/Localizable.strings en.lproj/Localizable.strings

Para más información ver: https://github.com/hiroshi/merge_strings

Algunos de ustedes lo encuentran útil, espero.

 2
Author: hiroshi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-07-25 18:15:32

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 de value: para el valor de texto predeterminado. El primer parámetro sirve como el ID de traducción. La ventaja de usar el parámetro value: es que el texto predeterminado se puede cambiar más tarde, pero el ID de traducción sigue siendo el mismo. El Localizable.el archivo strings contendrá "btn_yes" = "Yes";

Si el parámetro value: no se usó, entonces el primer parámetro se usaría para ambos: para el ID de traducción y también para el valor de texto predeterminado. El Localizable.el archivo strings contendría "Yes" = "Yes";. Este tipo de gestión de archivos de localización parece ser extraño. Especialmente si el texto traducido es largo, entonces el ID también es largo. Siempre que se cambie cualquier carácter del valor de texto predeterminado, también se cambiará el ID de traducción. Esto genera problemas cuando se utilizan sistemas de traducción externos. El cambio del ID de traducción se entiende como la adición de nuevo texto de traducción, que puede no ser siempre deseable.

 1
Author: petrsyn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-08-31 23:58:51
#define PBLocalizedString(key, val) \

[[NSBundle mainBundle] localizedStringForKey:(key) value:(val) table:nil]
 0
Author: baozhifei,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-06-27 09:25:12

Con iOS 7 & Xcode 5, debe evitar el uso de la 'Localización.strings 'method, y utilice el nuevo método' base localisation'. Hay algunos tutoriales alrededor si google para'localización base'

Apple doc: Localización base

 0
Author: Ronny Webers,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-09-10 16:46:55

Yo mismo, a menudo me dejo llevar por la codificación, olvidando poner las entradas en .archivos de cadenas. Por lo tanto, tengo scripts de ayuda para encontrar lo que debo poner de nuevo en .cadenas de archivos y traducir.

Como uso mi propia macro sobre NSLocalizedString, por favor revise y actualice el script antes de usar ya que asumí por simplicidad que nil se usa como un segundo parámetro para NSLocalizedString. La parte que querrías cambiar es

NSLocalizedString\(@(".*?")\s*,\s*nil\) 

Simplemente reemplácelo con algo que coincide con su uso de macro y NSLocalizedString.

Aquí viene el script, solo necesita 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 Localizable.archivo de cadenas. He aquí 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 compartiría.

 0
Author: Stanislav Dvoychenko,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-04-07 15:18:57

Si alguien busca una solución rápida. Es posible que desee comprobar mi solución que he reunido aquí: SwiftyLocalization

Con pocos pasos para configurar, 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 files> CSV files files> Localizable.cadenas

Además, también genera Localizables.swift, una estructura que actúa como interfaces para la recuperación de claves & decodificación para usted (Usted tiene que especificar manualmente una manera de decodificar la cadena de clave sin embargo).

¿Por qué es esto grande?

  1. Ya no es necesario tener una clave como una cadena simple en todos los lugares.
  2. Se detectan claves incorrectas en tiempo de compilación.
  3. Xcode puede hacer autocompletar.

Mientras que hay herramientas que pueden autocompletar su 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 el proyecto utiliza Google App Script para convertir hojas CSV > CSV, y Python script para convertir archivos CSV > > Localizable.cadenas Puede echar un vistazo rápido a esta hoja de ejemplo para saber qué es posible.

 0
Author: aunnnn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-10-13 06:12:42