Cómo forzar a NSLocalizedString a usar un idioma específico

Resuelto CodeFlakes asked hace 54 años • 28 respuestas

En iPhone NSLocalizedStringdevuelve la cadena en el idioma del iPhone. ¿Es posible forzar NSLocalizedStringel uso de un idioma específico para tener la aplicación en un idioma diferente al del dispositivo?

CodeFlakes avatar Jan 01 '70 08:01 CodeFlakes
Aceptado

NSLocalizedString()(y sus variantes) acceda a la clave "AppleLanguages" para NSUserDefaultsdeterminar cuáles son las configuraciones del usuario para los idiomas preferidos. Esto devuelve una serie de códigos de idioma, siendo el primero el establecido por el usuario para su teléfono y los siguientes utilizados como alternativas si un recurso no está disponible en el idioma preferido. (en el escritorio, el usuario puede especificar varios idiomas con un orden personalizado en Preferencias del Sistema)

Puede anular la configuración global para su propia aplicación si lo desea utilizando el método setObject:forKey: para configurar su propia lista de idiomas. Esto tendrá prioridad sobre el valor establecido globalmente y se devolverá a cualquier código de su aplicación que esté realizando la localización. El código para esto sería algo como:

[[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObjects:@"de", @"en", @"fr", nil] forKey:@"AppleLanguages"];
[[NSUserDefaults standardUserDefaults] synchronize]; //to make the change immediate

Esto haría que el alemán sea el idioma preferido para su solicitud, con el inglés y el francés como opciones alternativas. Le recomendamos llamar a esto en algún momento temprano en el inicio de su aplicación. Puede leer más sobre las preferencias de idioma/localización aquí: Temas de programación de internacionalización: obtención del idioma y la configuración regional actuales

Brian Webster avatar Nov 03 '2009 22:11 Brian Webster

Tuve el mismo problema recientemente y no quería iniciar ni parchear todo mi NSLocalizedString ni forzar el reinicio de la aplicación para que funcionara el nuevo idioma. Quería que todo funcionara como está.

Mi solución fue cambiar dinámicamente la clase del paquete principal y cargar allí el paquete apropiado:

Archivo de cabecera

@interface NSBundle (Language)
+(void)setLanguage:(NSString*)language;
@end

Implementación

#import <objc/runtime.h>

static const char _bundle=0;

@interface BundleEx : NSBundle
@end

@implementation BundleEx
-(NSString*)localizedStringForKey:(NSString *)key value:(NSString *)value table:(NSString *)tableName
{
    NSBundle* bundle=objc_getAssociatedObject(self, &_bundle);
    return bundle ? [bundle localizedStringForKey:key value:value table:tableName] : [super localizedStringForKey:key value:value table:tableName];
}
@end

@implementation NSBundle (Language)
+(void)setLanguage:(NSString*)language
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^
    {
        object_setClass([NSBundle mainBundle],[BundleEx class]);
    });
    objc_setAssociatedObject([NSBundle mainBundle], &_bundle, language ? [NSBundle bundleWithPath:[[NSBundle mainBundle] pathForResource:language ofType:@"lproj"]] : nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end

Básicamente, cuando se inicia tu aplicación y antes de cargar tu primer controlador, simplemente llama:

[NSBundle setLanguage:@"en"];

Cuando su usuario cambie su idioma preferido en su pantalla de configuración, simplemente llámelo nuevamente:

[NSBundle setLanguage:@"fr"];

Para restablecer los valores predeterminados del sistema, simplemente pase nil:

[NSBundle setLanguage:nil];

Disfrutar...

Para aquellos que necesitan una versión Swift:

var bundleKey: UInt8 = 0

class AnyLanguageBundle: Bundle {

    override func localizedString(forKey key: String,
                                  value: String?,
                                  table tableName: String?) -> String {

        guard let path = objc_getAssociatedObject(self, &bundleKey) as? String,
              let bundle = Bundle(path: path) else {

            return super.localizedString(forKey: key, value: value, table: tableName)
            }

        return bundle.localizedString(forKey: key, value: value, table: tableName)
    }
}

extension Bundle {

    class func setLanguage(_ language: String) {

        defer {

            object_setClass(Bundle.main, AnyLanguageBundle.self)
        }

        objc_setAssociatedObject(Bundle.main, &bundleKey,    Bundle.main.path(forResource: language, ofType: "lproj"), .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
    }
}
Gilad Novik avatar Nov 28 '2013 04:11 Gilad Novik

Normalmente hago esto de esta manera, pero DEBES tener todos los archivos de localización en tu proyecto.

@implementation Language

static NSBundle *bundle = nil;

+(void)initialize 
{
    NSUserDefaults* defs = [NSUserDefaults standardUserDefaults];
    NSArray* languages = [defs objectForKey:@"AppleLanguages"];
    NSString *current = [[languages objectAtIndex:0] retain];
    [self setLanguage:current];
}

/*
  example calls:
    [Language setLanguage:@"it"];
    [Language setLanguage:@"de"];
*/
+(void)setLanguage:(NSString *)l
{
    NSLog(@"preferredLang: %@", l);
    NSString *path = [[ NSBundle mainBundle ] pathForResource:l ofType:@"lproj" ];
    bundle = [[NSBundle bundleWithPath:path] retain];
}

+(NSString *)get:(NSString *)key alter:(NSString *)alternate 
{
    return [bundle localizedStringForKey:key value:alternate table:nil];
}

@end
Mauro Delrio avatar Nov 17 '2009 06:11 Mauro Delrio

No lo use en iOS 9. Esto devuelve cero para todas las cadenas que pasan a través de él.

Encontré otra solución que le permite actualizar las cadenas de idioma, sin reiniciar la aplicación y compatible con genstrings:

Coloque esta macro en Prefix.pch:

#define currentLanguageBundle [NSBundle bundleWithPath:[[NSBundle mainBundle] pathForResource:[[NSLocale preferredLanguages] objectAtIndex:0] ofType:@"lproj"]]

y donde sea que necesites una cadena localizada, usa:

NSLocalizedStringFromTableInBundle(@"GalleryTitleKey", nil, currentLanguageBundle, @"")

Para configurar el uso del idioma:

[[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObject:@"de"] forKey:@"AppleLanguages"];

Funciona incluso con saltos de idioma consecutivos como:

NSLog(@"test %@", NSLocalizedStringFromTableInBundle(@"NewKey", nil, currentLanguageBundle, @""));
[[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObject:@"fr"] forKey:@"AppleLanguages"];
NSLog(@"test %@", NSLocalizedStringFromTableInBundle(@"NewKey", nil, currentLanguageBundle, @""));
[[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObject:@"it"] forKey:@"AppleLanguages"];
NSLog(@"test %@", NSLocalizedStringFromTableInBundle(@"NewKey", nil, currentLanguageBundle, @""));
[[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObject:@"de"] forKey:@"AppleLanguages"];
NSLog(@"test %@", NSLocalizedStringFromTableInBundle(@"NewKey", nil, currentLanguageBundle, @""));
Tudor avatar Oct 28 '2011 18:10 Tudor

Como dije antes, simplemente haz:

[[NSUserDefaults standardUserDefaults] setObject: [NSArray arrayWithObjects:@"el", nil] forKey:@"AppleLanguages"];

Pero para evitar tener que reiniciar la aplicación, coloque la línea en el método principal de main.m, justo antes de UIApplicationMain(...).

Frédéric Feytons avatar Jun 25 '2010 12:06 Frédéric Feytons