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

Respuestas:

261

NSLocalizedString()(y sus variantes) acceda a la clave "AppleLanguages" NSUserDefaultspara determinar 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 retrocesos si un recurso no está disponible en el idioma preferido. (en el escritorio, el usuario puede especificar varios idiomas con un pedido 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 en su aplicación que esté realizando la localización. El código para esto se vería así:

[[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 aplicación, con inglés y francés como respaldo. Debería llamar a esto en algún momento temprano en el inicio de su aplicación. Puede leer más sobre las preferencias de idioma / idioma aquí: Temas de programación de internacionalización: Obtener el idioma y el idioma actuales

Brian Webster
fuente
44
Esto no funciona para mí, usará el idioma predeterminado sin importar qué.
quano
50
Denniss Parece funcionar mejor si configura la preferencia de idioma antes de iniciar la aplicación. Lo hago en la función main (), antes de que se llame a UIApplicationMain (). Una vez que se esté ejecutando, no cambiará el idioma utilizado, sino que solo establecerá la preferencia para la próxima vez.
geon
3
Debe configurar el idioma antes de inicializar UIKit y debe especificar una configuración regional completa de idioma + región. Consulte esto para ver un ejemplo completo blog.federicomestrone.com/2010/09/15/…
alimentado el
18
establecer AppleLanguages ​​cambiará un idioma en tiempo de ejecución si se hace en main () - ¡cierto! pero ... la PRIMERA VEZ que se instala la aplicación, los valores predeterminados de nsuser se inicializan DESPUÉS de que se llame a la aplicación Main (), que ignorará los idiomas de Apple configurados previamente y requerirá un feo reinicio forzado de la aplicación para ver el idioma deseado.
JohnPayne
3
Esto no funciona con elementos plurales definidos en Localizable.stringsdict. ¿Alguna idea de si hay una posible solución?
Mihai Fratu
147

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

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

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 su aplicación se inicia y antes de cargar su primer controlador, simplemente llame:

[NSBundle setLanguage:@"en"];

Cuando su usuario cambie su idioma preferido en la pantalla de configuración, simplemente vuelva a llamarlo:

[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
fuente
77
Hola @Wirsing, hasta ahora me funciona muy bien. Incluso he subido una aplicación a la tienda y Apple no se queja.
Gilad Novik
77
Esta es una gran solución, también estoy haciendo referencia a esto aquí: medium.com/ios-apprentice/905e4052b9de
James Tang
1
Hola @JamesTang, gracias por la referencia y tu publicación. Encontré mucha información útil sobre la localización.
Gilad Novik
3
@Gilad su método funciona perfecto para cadenas dinámicas (cadenas que definí en localizable.strings) pero en cuanto a los botones y etiquetas del guión gráfico solo funciona en el método principal. Entonces, ¿puede extender su respuesta para incluir la localización de controles de UI? Me refiero a cómo actualizar (sin cerrar) el guión gráfico después de invocar [NSBundle setLanguage: @ "??"] ;?
Jawad Al Shaikh
2
En cuanto a XIB / storyboard: debe asegurarse de llamar a este método ANTES de cargar la vista. En cuanto a la actualización de vistas en vivo: en mi caso, simplemente volví a cargar el controlador de vista (lo puse en un controlador de navegación y simplemente reemplacé el controlador. Al volver a cargarlo, obtuvo las nuevas cadenas traducidas). Estoy trabajando en un cocoapod para ello, probablemente también incluiré un ejemplo allí. Estén atentos ...
Gilad Novik
137

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
fuente
1
+1. Este es un truco realmente agradable que no he visto en ningún otro lado. Al crear un "subpaquete" a partir de una de las carpetas de localización, puede hacer que las cadenas funcionen bien siempre que envuelva NSLocalizedString con algo que se desvíe aquí.
Ben Zotto
44
Excelente mauro. Noté que también puede funcionar para archivos de su proyecto. Si por alguna razón (como en mi caso), necesita descargar archivos de cadenas de la red y almacenarlos en su directorio 'Documentos' (con la estructura de carpetas Documents / en.lproj / Localizable.strings, Documents / fr.lproj / Cadenas localizables, ...). Aunque puedes hacer un NSBundle. Simplemente use este código para la ruta: NSString * ruta = [NSHomeDirectory () stringByAppendingPathComponent: [NSString stringWithFormat: @ "/ Documents /% @. Lproj", l, nil]];
arnaud del.
1
Me parece que este es el mejor enfoque, junto con la contribución de Alessandro a continuación. No es tan intrusivo y no debería requerir un reinicio.
PKCLsoft
1
¿Qué estoy haciendo mal? Hice una clase de lenguaje, heredando NSObject, puse esto en la implementación, puse los nombres de los métodos en .h, luego puse [Language setLanguage: @ "es"]; en mi botón Cambiar idioma, el código se ejecuta (se llama al método de botón y va a la clase Idioma), pero no hace nada. Tengo mi localizable.strings (español) configurado también. Pero parece estar funcionando para muchas otras personas. Por favor, que alguien me haga tonterías.
SirRupertIII
1
@KKendall lo llamas así: [Language setLanguage: @ "es"]; NSString * stringToUse = [Language get: @ "Your Text" alter: nil];
Mikrasya
41

No lo use en iOS 9. Esto devuelve nulo para todas las cadenas pasadas a través de él.

He encontrado otra solución que le permite actualizar las cadenas de idioma, sin reiniciar la aplicación y compatible con cadenas genéricas:

Pon esta macro en el Prefix.pch:

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

y donde sea que necesite un uso de cadena localizado:

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
fuente
2
@ powerj1984: no, solo cambiará las cadenas en sus archivos de origen. Si desea cambiar los idiomas xib, debe volver a cargar los xibs a mano, desde el paquete de su idioma seleccionado.
gklka
@gklka He seguido los pasos dados por Tudozier, pero mis XIB no están cambiando el idioma, como le dijo a Recargar XIB a mano, ¿qué significa realmente recargar XIB?
Dk Kumar
@DkKumar: Tienes que hacer algo similar a esto: stackoverflow.com/questions/21304988/…
gklka
@gklka Su respuesta parece correcta y he seguido los mismos pasos que describió en su respuesta, pero el problema es que si cambiamos la ruta del paquete, encontrará todos los recursos (es decir, el archivo .png utilizado en XIB) en el mismo paquete, por lo que tenemos que copiar en exceso la carpeta de imágenes en todo el paquete, como en.lproj, es.lproj, etc., lo que afectará al aumento del tamaño de IPA, ¿hay alguna forma de superar este problema? como dijiste en tu publicación, déjame probar esto y dejarte más sobre esto Gracias por ayudarme por lo mismo
Dk Kumar
1
Esto se rompe horriblemente en iOS 9, devolviendo el valor nulo para todas las cadenas.
Alex Zavatone
32

Como se dijo anteriormente, solo 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 UIApplicationMain(...).

Frédéric Feytons
fuente
2
Respuesta muy útil! PD Puede sonar obvio para los no principiantes, pero debe insertar esa línea después de que se NSAutoreleasePool * pool ..filtren algunos objetos que se liberen automáticamente.
pt2ph8
1
IMPORTANTE: Esto no funcionará si llamas [[NSBundle mainBundle] URLForResource:withExtension:]antes.
Kof
3
¿Qué pasa si se usa Swift, entonces no hay main.m?
probado el
@turingtested ¿Has probado Swift y Storyboard? ¿Funciona?
iDevAmit
Hola, cómo localizar el texto dinámico que proviene del servidor, en mi aplicación, cada texto que tengo que mostrar en chino simplificado, pero cómo mostrar el texto en chino que viene en inglés. ayuadame.
Mahesh Narla
31

El truco para usar un idioma específico seleccionándolo desde la aplicación es forzar el NSLocalizedStringuso de un paquete específico dependiendo del idioma seleccionado,

Aquí está la publicación que he escrito para esta localización de avance de aprendizaje en aplicaciones ios

y aquí está el código de una localización de avance de aplicación de muestra en aplicaciones ios

objeto2.0
fuente
También funciona en iOS7, tengo que cambiar de la solución de Brian a esto, ya que parece que iOS anula la opción de idiomas nuevamente, por lo que me quedé con la configuración de idioma por primera vez. ¡Tu solución funciona a las mil maravillas!
haxpor
14

¿Qué opinas sobre esta solución para Swift 3?

extension String {

    func localized(forLanguage language: String = Locale.preferredLanguages.first!.components(separatedBy: "-").first!) -> String {

        guard let path = Bundle.main.path(forResource: language == "en" ? "Base" : language, ofType: "lproj") else {

            let basePath = Bundle.main.path(forResource: "Base", ofType: "lproj")!

            return Bundle(path: basePath)!.localizedString(forKey: self, value: "", table: nil)
        }

        return Bundle(path: path)!.localizedString(forKey: self, value: "", table: nil)
    }
}

Uso simple:

"report".localized(forLanguage: "pl") //forced language
"report".localized() //default language selected by user in settings, in case when your app doesnt support selected lanaguage, the default one is selected, here is an english.
Bartłomiej Semańczyk
fuente
Hola, cómo localizar el texto dinámico que proviene del servidor, en mi aplicación, cada texto que tengo que mostrar en chino simplificado, pero cómo mostrar el texto en chino que viene en inglés. ayuadame.
Mahesh Narla
Esta es una respuesta brillante;)
Bartłomiej Semańczyk
gran respuesta me ayudó
Dilip Tiwari
13

Como menciona Brian Webster, el idioma debe establecerse "en algún momento temprano en el inicio de su aplicación". Pensé applicationDidFinishLaunching:de la AppDelegateque debería ser un lugar adecuado para hacerlo, ya que es donde hago todos los demás inicialización.

Pero como menciona William Denniss, eso parece tener efecto solo después de que se reinicia la aplicación, lo cual es inútil.

Sin embargo, parece funcionar bien si pongo el código en la función principal:

int main(int argc, char *argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    // Force language to Swedish.
    [[NSUserDefaults standardUserDefaults]
     setObject:[NSArray arrayWithObject:@"sv"]
     forKey:@"AppleLanguages"];

    int retVal = UIApplicationMain(argc, argv, nil, nil);
    [pool release];
    return retVal;
}

Agradecería cualquier comentario sobre esto.

geon
fuente
en mi implementación es algo configurable por el usuario, así que solo aparece un cuadro de diálogo que les dice que deberán reiniciar :)
William Denniss
Funciona, casi para todos, excepto la imagen localizada Default.png.
Lukasz
2
Si permite que los usuarios configuren un idioma y luego les dice que reinicien la aplicación, recuerde que los valores predeterminados de NSUser podrían no guardarse si se reinician demasiado rápido. La mayoría de los usuarios no son tan rápidos, pero cuando estás probando la aplicación puedes ser demasiado rápido y ver como resultado un comportamiento inconsistente. ¡Me llevó un par de horas darme cuenta de por qué mi cambio de idioma a veces funcionaba y otras no!
arlomedia
3
Eso no es un problema, solo úselo [[NSUserDefaults standardUserDefaults] synchronize];después de llamarsetObject:forKey:
Ben Baron,
11

Me gusta más el método de Mauro Delrio. También he agregado lo siguiente en mi Project_Prefix.pch

#import "Language.h"    
#define MyLocalizedString(key, alt) [Language get:key alter:alt]

Entonces, si alguna vez desea usar el método estándar (que usa NSLocalizedString), puede hacer una sustitución rápida de sintaxis en todos los archivos.

dnaxxx
fuente
1
También me gusta su método y agrego un método para cargar imágenes png: + (UIImage ) imageNamed: (NSString *) imageFileName {NSString fullPath = [bundle pathForResource: imageFileName ofType: @ "png"]; UIImage * imageObj = [[UIImage alloc] initWithContentsOfFile: fullPath]; return [lanzamiento automático de imageObj]; }
Jagie
11

NSLocalizedString()lee el valor de la clave AppleLanguagesde los valores predeterminados estándar del usuario ( [NSUserDefaults standardUserDefaults]). Utiliza ese valor para elegir una localización adecuada entre todas las localizaciones existentes en tiempo de ejecución. Cuando Apple crea el diccionario predeterminado del usuario en el inicio de la aplicación, busca la clave de idioma (s) preferido en las preferencias del sistema y copia el valor desde allí. Esto también explica, por ejemplo, por qué cambiar la configuración de idioma en OS X no tiene ningún efecto en la ejecución de aplicaciones, solo en las aplicaciones iniciadas a partir de entonces. Una vez copiado, el valor no se actualiza solo porque la configuración cambia. Es por eso que iOS reinicia todas las aplicaciones si cambia el idioma.

Sin embargo, todos los valores del diccionario predeterminado del usuario pueden sobrescribirse mediante argumentos de línea de comandos. Ver NSUserDefaultsdocumentación en el NSArgumentDomain. Esto incluso incluye aquellos valores que se cargan desde el archivo de preferencias de la aplicación (.plist). Es realmente bueno saber si desea cambiar un valor solo una vez para la prueba .

Entonces, si desea cambiar el idioma solo para las pruebas, probablemente no quiera alterar su código (si olvida eliminar este código más adelante ...), en su lugar, dígale a Xcode que inicie su aplicación con parámetros de línea de comando ( por ejemplo, use la localización en español):

ingrese la descripción de la imagen aquí

No es necesario tocar su código en absoluto. Simplemente cree diferentes esquemas para diferentes idiomas y puede iniciar rápidamente la aplicación una vez en un idioma y otra en otro simplemente cambiando el esquema.

Mecki
fuente
Funciona solo por primera vez después de eso, nuevamente se carga en la preferencia del dispositivo. idioma ..
Aks
@Aks funciona todo el tiempo para mí. Asegúrese de que está comenzando el esquema correcto, asegúrese de comenzar desde dentro de Xcode (los relanzamientos, las bifurcaciones, etc. no obtendrán el argumento) y asegúrese de no anular el idioma Optionscomo en las versiones más nuevas de Xcode que Apple ofrece que también.
Mecki
Gracias por su respuesta @Mecki ... Para mí no funciona en xcode, pero cuando lo menciono en el código, funciona ... Pero aún así, si configuré mi dispositivo con otros idiomas preferidos, es necesario reiniciar la aplicación para cargar en mi preferido lenguaje que paso en argumentos ...
Aks
@Aks Al usar eso en un dispositivo, solo funcionará cuando se inicie directamente desde Xcode, no funcionará cuando inicie la aplicación nuevamente tocando su icono en algún dispositivo y tampoco funcionará cuando la aplicación se cierre (p. Ej. porque pasó a segundo plano) y se reinicia por el sistema (ya que este reinicio no es un reinicio realizado por Xcode), por lo que cambiar a una aplicación diferente y viceversa puede no funcionar si iOS tiene que matar su aplicación en el medio (por ejemplo, porque necesita la memoria).
Mecki
10

Se me ocurrió una solución que te permite usar NSLocalizedString. Creo una categoría de NSBundlellamada NSBundle+RunTimeLanguage. La interfaz es así.

// NSBundle+RunTimeLanguage.h
#import <Foundation/Foundation.h>
@interface NSBundle (RunTimeLanguage)
#define NSLocalizedString(key, comment) [[NSBundle mainBundle] runTimeLocalizedStringForKey:(key) value:@"" table:nil]
- (NSString *)runTimeLocalizedStringForKey:(NSString *)key value:(NSString *)value table:(NSString *)tableName;
@end

La implementación es así.

// NSBundle+RunTimeLanguage.m
#import "NSBundle+RunTimeLanguage.h"
#import "AppDelegate.h"

@implementation NSBundle (RunTimeLanguage)

- (NSString *)runTimeLocalizedStringForKey:(NSString *)key value:(NSString *)value table:(NSString *)tableName
{
    AppDelegate *appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
    NSString *path= [[NSBundle mainBundle] pathForResource:[appDelegate languageCode] ofType:@"lproj"];
    NSBundle *languageBundle = [NSBundle bundleWithPath:path];
    NSString *localizedString=[languageBundle localizedStringForKey:key value:key table:nil];
    return localizedString;
}
@end

Que simplemente agregar importación NSBundle+RunTimeLanguage.hen los archivos que usan NSLocalizedString.

Como puede ver, guardo mi languageCode en una propiedad de AppDelegate. Esto podría almacenarse en cualquier lugar que desee.

Lo único que no me gusta es una Advertencia que NSLocalizedStringredefinió el marco. Quizás alguien podría ayudarme a arreglar esta parte.

qman64
fuente
1
Agregue #undef NSLocalizedStringjusto antes #definepara deshabilitar la advertencia
berilio
¿Funciona esto en la localización del archivo xib? Tengo un archivo .xib y .strings para traducir el nombre de la interfaz de usuario en un idioma en particular. Lo intenté y no funciona con XIB, pero no estoy seguro de haber hecho las cosas correctamente
Kong Hantrakool
Kong, perdón por la respuesta tardía. Esto funciona donde sea que use NSLocalizedString. Por lo tanto, no funcionará directamente en una XIB. Necesitaría IBOutlets para las cadenas en el XIB y luego tendría que establecer mediante programación los valores de cadena en el código.
qman64
Este enfoque funcionó para mí y pude cambiar los idiomas sobre la marcha. Tenga en cuenta que los códigos de idioma como "es", fr "y" en "funcionaron bien. Pero" zh "(para chino simplificado) falló y me devolvió las cadenas nulas. Hice una búsqueda global en mi sistema de" es.lproj "para ver dónde están los archivos que estaba accediendo eran y yo descubrimos que hay que "zh.lproj" es en realidad "zh-Hans.lproj".
Gallymon
9

Versión rápida:

NSUserDefaults.standardUserDefaults().setObject(["fr"], forKey: "AppleLanguages")
NSUserDefaults.standardUserDefaults().synchronize()
daaniaal
fuente
8

En pocas palabras:

Localiza tu aplicación

Es lo primero que debe hacer es localizar su aplicación con al menos dos idiomas (inglés y francés en este ejemplo).

Anular NSLocalizedString

En su código, en lugar de usar NSLocalizedString(key, comment), use una macro MYLocalizedString(key, comment)definida como esta: #define MYLocalizedString(key, comment) [[MYLocalizationSystem sharedInstance] localizedStringForKey:(key) value:(comment)];

Este MYLocalizationSystemsingleton hará lo siguiente:

  • Establezca el idioma configurando el usuario NSBundle localizado correcto que solicita
  • Devuelve el NSString localizado de acuerdo con este idioma previamente establecido

Establecer idioma de usuario

Cuando el usuario cambió el idioma de la aplicación en francés, llame al [[MYLocalizationSystem sharedInstance] setLanguage:@"fr"];

- (void)setLanguage:(NSString *)lang
{
    NSString *path = [[NSBundle mainBundle] pathForResource:lang ofType:@"lproj"];
    if (!path)
    {
        _bundle = [NSBundle mainBundle];
        NSLog(@"Warning: No lproj for %@, system default set instead !", lang);
        return;
    }

    _bundle = [NSBundle bundleWithPath:path];
}

En este ejemplo, este método establece el paquete localizado en fr.lproj

Devolver cadena localizada

Una vez que haya configurado el paquete localizado, podrá obtener la cadena localizada correcta de él con este método:

- (NSString *)localizedStringForKey:(NSString *)key value:(NSString *)value
{
    // bundle was initialized with [NSBundle mainBundle] as default and modified in setLanguage method
    return [self.bundle localizedStringForKey:key value:value table:nil];
}

Espero que esto te ayudará.

Encontrará más detalles en este artículo de NSWinery.io

usuario3465357
fuente
Le pedimos que no solo enlace a una solución en su respuesta; el enlace podría dejar de funcionar algún día. Si bien no tiene que eliminar el enlace de su respuesta, le pedimos que edite su respuesta para incluir un resumen de la sección relevante del artículo vinculado.
Oblivious Sage
7

Extensiones Swift 3:

extension Locale {
    static var preferredLanguage: String {
        get {
            return self.preferredLanguages.first ?? "en"
        }
        set {
            UserDefaults.standard.set([newValue], forKey: "AppleLanguages")
            UserDefaults.standard.synchronize()
        }
    }
}

extension String {
    var localized: String {

    var result: String

    let languageCode = Locale.preferredLanguage //en-US

    var path = Bundle.main.path(forResource: languageCode, ofType: "lproj")

    if path == nil, let hyphenRange = languageCode.range(of: "-") {
        let languageCodeShort = languageCode.substring(to: hyphenRange.lowerBound) // en
        path = Bundle.main.path(forResource: languageCodeShort, ofType: "lproj")
    }

    if let path = path, let locBundle = Bundle(path: path) {
        result = locBundle.localizedString(forKey: self, value: nil, table: nil)
    } else {
        result = NSLocalizedString(self, comment: "")
    }
        return result
    }
}

Uso:

Locale.preferredLanguage = "uk"

label.text = "localizedKey".localized
ChikabuZ
fuente
Gracias. Trabajando bien.
Krunal Nagvadia
5

En el archivo .pch para definir:

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


#define NSLocalizedString(str,nil) NSLocalizedStringFromTableInBundle(str, nil, currentLanguageBundle, @"")
MrPiliffo
fuente
¿Cómo me deshago de la advertencia 'ovverride ...'?
Idan Moshe
1
La solución simple hace todo lo que necesito. Pude usar esto como una guía para resolver mi problema. Si pathForResource devuelve nil porque no tengo un archivo de localización, entonces uso pathForResource: @ "Base" ya que nada más he intentado extraer cadenas de la localización base. Gracias.
GRW
Esto es hacky (al igual que todas las otras respuestas), y fue más fácil de implementar en la prueba de unidad que estaba escribiendo.
Max
4

Tal vez debería complementar con esto (en el archivo .pch después de #import):

extern NSBundle* bundle; // Declared on Language.m

#ifdef NSLocalizedString
    #undef NSLocalizedString
    // Delete this line to avoid warning
    #warning "Undefining NSLocalizedString"
#endif

#define NSLocalizedString(key, comment) \
    [bundle localizedStringForKey:(key) value:@"" table:nil]
D33pN16h7
fuente
/Users/pwang/Desktop/Myshinesvn/Myshine/viewController/OrientationReadyPagesView.m:193:31: Uso del identificador no declarado 'paquete'
pengwang
4

Solución Swift 3:

let languages = ["bs", "zh-Hant", "en", "fi", "ko", "lv", "ms", "pl", "pt-BR", "ru", "sr-Latn", "sk", "es", "tr"]
UserDefaults.standard.set([languages[0]], forKey: "AppleLanguages")

Dio algunos ejemplos de códigos de idiomas que se pueden usar. Espero que esto ayude

Jeremy Bader
fuente
3

Puede crear un subpaquete con el conjunto de cadenas localizadas con las que desea hacer esto y luego usarlo NSLocalizedStringFromTableInBundle()para cargarlas. (Supongo que este es un contenido separado de la localización normal de la interfaz de usuario que podría estar haciendo en la aplicación).

Sixten Otto
fuente
3

para mi caso tengo dos archivos localizados, ja y en

y me gustaría forzarlo a que sea si el idioma preferido en el sistema no es ni ja

voy a editar el archivo main.m

comprobaré si el primer preferido es en o ja, de lo contrario, cambiaré el segundo idioma preferido a en.

int main(int argc, char *argv[])
{

    [[NSUserDefaults standardUserDefaults] removeObjectForKey:@"AppleLanguages"];
    [[NSUserDefaults standardUserDefaults] synchronize];

    NSString *lang = [[NSLocale preferredLanguages] objectAtIndex:0];

    if (![lang isEqualToString:@"en"]  &&  ![lang isEqualToString:@"ja"]){

        NSMutableArray *array = [[NSMutableArray alloc] initWithArray:[NSLocale preferredLanguages]];
        [array replaceObjectAtIndex:1 withObject:@"en"];

        [[NSUserDefaults standardUserDefaults] setObject:array forKey:@"AppleLanguages"];
        [[NSUserDefaults standardUserDefaults] synchronize];


    }

    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));


    }


}
chings228
fuente
Gracias @ chings228 es útil para mí, pero esto no cambia la imagen de inicio predeterminada (splash) para la aplicación. Tengo diferentes splashs para cada idioma. ¿sabes cómo solicitar esto?
JERC
Creo que las carreras de salpicaduras antes de que el programa principal empieza a funcionar, así que no sé cómo reemplazar él, puede ser de ayuda puede plist pero puede trabajar para la próxima vez que la aplicación se abre
chings228
2

Puedes hacer algo como esto:

NSString *bundlePath = [[NSBundle mainBundle] pathForResource:@"Localizable" ofType:@"strings" inDirectory:nil forLocalization:@"es"];


NSBundle *spanishBundle = [[NSBundle alloc] initWithPath:[bundlePath stringByDeletingLastPathComponent]];

NSLocalizedStringFromTableInBundle(@"House", nil, spanishBundle, nil):
JERC
fuente
2

Basado en la respuesta de Tudorizer para cambiar el idioma sin salir o reiniciar la aplicación.

En lugar de una macro, use una clase para acceder al idioma preferido para verificar si hay un código de idioma específico.

A continuación se muestra una clase utilizada para obtener el paquete de idioma actual que funciona para iOS 9:

@implementation OSLocalization

+ (NSBundle *)currentLanguageBundle
{
    // Default language incase an unsupported language is found
    NSString *language = @"en";

    if ([NSLocale preferredLanguages].count) {
        // Check first object to be of type "en","es" etc
        // Codes seen by my eyes: "en-US","en","es-US","es" etc

        NSString *letterCode = [[NSLocale preferredLanguages] objectAtIndex:0];

        if ([letterCode rangeOfString:@"en"].location != NSNotFound) {
            // English
            language = @"en";
        } else if ([letterCode rangeOfString:@"es"].location != NSNotFound) {
            // Spanish
            language = @"es";
        } else if ([letterCode rangeOfString:@"fr"].location != NSNotFound) {
            // French
            language = @"fr";
        } // Add more if needed
    }

    return [NSBundle bundleWithPath:[[NSBundle mainBundle] pathForResource:language ofType:@"lproj"]];
}

/// Check if preferred language is English
+ (BOOL)isCurrentLanguageEnglish
{
    if (![NSLocale preferredLanguages].count) {
        // Just incase check for no items in array
        return YES;
    }

    if ([[[NSLocale preferredLanguages] objectAtIndex:0] rangeOfString:@"en"].location == NSNotFound) {
        // No letter code for english found
        return NO;
    } else {
        // Tis English
        return YES;
    }
}

/*  Swap language between English & Spanish
 *  Could send a string argument to directly pass the new language
 */
+ (void)changeCurrentLanguage
{
    if ([self isCurrentLanguageEnglish]) {
        [[NSUserDefaults standardUserDefaults] setObject:@[@"es"] forKey:@"AppleLanguages"];
    } else {
        [[NSUserDefaults standardUserDefaults] setObject:@[@"en"] forKey:@"AppleLanguages"];
    }
}
@end

Use la clase anterior para hacer referencia a un archivo de cadena / imagen / video / etc.

// Access a localized image
[[OSLocalization currentLanguageBundle] pathForResource:@"my_image_name.png" ofType:nil]
// Access  a localized string from Localizable.strings file
NSLocalizedStringFromTableInBundle(@"StringKey", nil, [OSLocalization currentLanguageBundle], @"comment")

Cambie el idioma en línea como se muestra a continuación o actualice el método "changeCurrentLanguage" en la clase anterior para tomar un parámetro de cadena que haga referencia al nuevo idioma.

[[NSUserDefaults standardUserDefaults] setObject:@[@"es"] forKey:@"AppleLanguages"];
Antonioni
fuente
2

En swift 4, lo resolví sin necesidad de reiniciar o usar bibliotecas.

Después de probar muchas opciones, encontré esta función, donde pasa el stringToLocalize (de Localizable.String, el archivo de cadenas) que desea traducir, y el idioma en el que desea traducirlo, y lo que devuelve es el valor para esa cadena que tienes en el archivo de cadenas:

    func localizeString (stringToLocalize: String, language: String) -> String
    {
        let path = Bundle.main.path (forResource: language, ofType: "lproj")
        let languageBundle = Bundle (path: path!)
        return languageBundle! .localizedString (forKey: stringToLocalize, value: "", table: nil)
    }

Teniendo en cuenta esta función, creé esta función en un archivo Swift:

struct CustomLanguage {

    func createBundlePath () -> Bundle {
        let selectedLanguage = //recover the language chosen by the user (in my case, from UserDefaults)
        let path = Bundle.main.path(forResource: selectedLanguage, ofType: "lproj")
        return Bundle(path: path!)!
    }
}

Para acceder desde toda la aplicación, y en cada cadena del resto de ViewControllers, en lugar de poner:

NSLocalizedString ("StringToLocalize", comment: ")

Lo he reemplazado con

let customLang = CustomLanguage() //declare at top
let bundleLanguage = customLang.createBundle()

NSLocalizedString("StringToLocalize", tableName: nil, bundle: bundleLanguage, value: "", comment: “”) //use in each String

No sé si es la mejor manera, pero lo encontré muy simple y funciona para mí, ¡espero que te ayude!

ainareta685
fuente
1

Esta función intentará obtener una cadena localizada para el idioma actual y, si no se encuentra, la utilizará en inglés.

- (NSString*)L:(NSString*)key
{
    static NSString* valueNotFound = @"VALUE_NOT_FOUND";
    static NSBundle* enBundle = nil;

    NSString* pl = [NSLocale preferredLanguages][0];
    NSString* bp = [[NSBundle mainBundle] pathForResource:pl ofType:@"lproj"];
    NSBundle* b = [NSBundle bundleWithPath:bp];

    NSString* s = [b localizedStringForKey:key value:valueNotFound table:nil];
    if ( [s isEqualToString:valueNotFound] ) {
        if ( !enBundle ) {
            bp = [[NSBundle mainBundle] pathForResource:@"en" ofType:@"lproj"];
            enBundle = [NSBundle bundleWithPath:bp];
        }
        s = [enBundle localizedStringForKey:key value:key table:nil];
    }

    return s;
}
Cherpak Evgeny
fuente
1

Quería agregar soporte para un idioma que no es oficialmente compatible con iOS (no aparece en la sección Idioma en la configuración del sistema). Siguiendo el Tutorial de internacionalización de Apple y algunos consejos aquí de Brian Webster y geon, se me ocurrió este código (póngalo en main.m):

int main(int argc, char * argv[]) {
    @autoreleasepool {
        // Grab regional settings locale, for Slovenian this is either sl_SI or en_SI
        NSLocale *locale = [NSLocale currentLocale];
        NSString *ll = [locale localeIdentifier]; // sl_SI

        // Grab the first part of language identifier
        NSArray *comp = [ll componentsSeparatedByString:@"_"];
        NSString *ll1 = @"en";
        if (comp.count > 0) {
            ll1 = comp[0]; // sl, en, ...
        }
        // Check if we already saved language (user can manually change it inside app for example)
        if (![[NSUserDefaults standardUserDefaults] objectForKey:@"SelectedLanguage"]) {
            //   Slovenian (Slovenia),            Slovenia
            if ([ll isEqualToString:@"sl_SI"] || [ll isEqualToString:@"en_SI"]) {
                ll1 = @"sl-SI"; // This is the part of localized path for Slovenian language that Xcode generates
            }
            // Add more unsupported languages here...

            [[NSUserDefaults standardUserDefaults] setObject:ll1 forKey:@"SelectedLanguage"]; // Save language
        }
        else {
            // Restore language as we have previously saved it
            ll1 = [[NSUserDefaults standardUserDefaults] objectForKey:@"SelectedLanguage"];
        }
        // Overwrite NSLocalizedString and StoryBoard language preference
        [[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObjects:ll1, @"en", @"fr", nil] forKey:@"AppleLanguages"];
        // Make sure settings are stored to disk
        [[NSUserDefaults standardUserDefaults] synchronize];

        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}

Esto funciona bien tanto para Storyboard como para el código NSLocalizedString. El código supone que el usuario tendrá una opción para cambiar manualmente el idioma dentro de la aplicación más adelante.

Por supuesto, no olvide agregar las traducciones adecuadas de Storyboard y las traducciones de Localizable.strings (consulte el enlace a la página de Apple anterior para saber cómo hacerlo).

frin
fuente
Veo que también tiene problemas con el idioma esloveno :) Para mejorar esta respuesta para los idiomas no compatibles con iOS, ¿ha descubierto si podemos establecer cadenas personalizables que se utilizarán para los controles del sistema en la aplicación (Editar, Listo, Más, ...)?
miham
1

Aquí hay una solución decente para este problema, y ​​no requiere reiniciar la aplicación.

https://github.com/cmaftuleac/BundleLocalization

Esta implementación funciona ajustando dentro de NSBundle. La idea es que anule el método localizedStringForKey en la instancia del objeto NSBundle, y luego llame a este método en un paquete diferente con un idioma diferente. Simple y elegante totalmente compatible con todo tipo de recursos.

Corneliu Maftuleac
fuente
Este código del proyecto me ayudó mucho. Cómo encontrar un paquete para un código de idioma específico ---NSString *path = [[NSBundle mainBundle] pathForResource:lang ofType:@"lproj" ];
Eonil
Esta clase funciona bien, pero después de cambiar el idioma a otro, no tiene efecto.
Anilkumar iOS ReactNative
0

haga lo que haga, la mejor manera es tomar el nombre_corto para el idioma especificado, es decir: fr, en, nl, de, it, etc. y asignarlo a un valor global.

cree una vista de selector para que aparezca como un menú desplegable (combinación de un botón al hacer clic en el que aparece una vista de selector desde abajo con una lista de idiomas) y seleccione el idioma que desee. deje que el nombre corto se almacene internamente. cree un archivo .h + .m llamado LocalisedString.

Establezca el valor global de short_name para que sea igual al valor obtenido en LocalisedString.m Cuando se selecciona el idioma requerido, asigne NSBundlePath para crear subdirectorios de proyecto para el idioma necesario. por ejemplo, nl.proj, en.proj.

Cuando se selecciona la carpeta de proyecto particular, llame a la cadena localizada para el idioma respectivo y cambie el idioma dinámicamente.

No hay reglas rotas.

Abhijeet
fuente
0

Para Swift, puede anular el main.swiftarchivo y establecer la cadena UserDefaults allí antes de que se ejecute la aplicación. De esta manera, no tiene que reiniciar la aplicación para ver el efecto deseado.

import Foundation
import UIKit

// Your initialisation code here
let langCultureCode: String = "LANGUAGE_CODE"

UserDefaults.standard.set([langCultureCode], forKey: "AppleLanguages")
UserDefaults.standard.synchronize()

UIApplicationMain(CommandLine.argc, CommandLine.unsafeArgv, nil, NSStringFromClass(AppDelegate.self))

emparejado junto con la eliminación de @UIApplicationMainen su AppDelegate.swiftarchivo.

Andreas
fuente