Como forçar o NSLocalizedString a usar um idioma específico

Respostas:

261

NSLocalizedString()(e variantes dos mesmos) acessam a tecla "AppleLanguages" NSUserDefaultspara determinar quais são as configurações do usuário para os idiomas preferidos. Isso retorna uma matriz de códigos de idioma, sendo o primeiro o definido pelo usuário para o telefone e os subsequentes usados ​​como fallbacks se um recurso não estiver disponível no idioma preferido. (na área de trabalho, o usuário pode especificar vários idiomas com um pedido personalizado em Preferências do sistema)

Você pode substituir a configuração global do seu próprio aplicativo, se desejar, usando o método setObject: forKey: para definir sua própria lista de idiomas. Isso terá precedência sobre o valor definido globalmente e será retornado para qualquer código em seu aplicativo que esteja executando a localização. O código para isso seria algo como:

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

Isso tornaria o alemão o idioma preferido para o seu aplicativo, com inglês e francês como substitutos. Você gostaria de chamar isso algum dia no início da inicialização do seu aplicativo. Você pode ler mais sobre as preferências de idioma / localidade aqui: Tópicos de programação de internacionalização: obtendo o idioma e o local atuais

Brian Webster
fonte
4
Isso não funciona para mim, ele usará o idioma padrão, não importa o quê.
quano
50
Denniss; Parece funcionar melhor se você definir a preferência de idioma antes do lançamento do aplicativo. Eu faço isso na função main (), antes que UIApplicationMain () seja chamado. Quando estiver realmente em execução, ele não mudará o idioma usado, mas apenas definirá a preferência para a próxima vez.
geon
3
É necessário definir o idioma antes de inicializar o UIKit e você deve especificar um idioma completo + localidade da região - confira este exemplo completo blog.federicomestrone.com/2010/09/15/…
fedmest
18
configurar AppleLanguages ​​mudará um idioma em tempo de execução se for feito em main () - true! mas ... na PRIMEIRA VEZ que o aplicativo for instalado, os nsuserdefaults serão inicializados APÓS o UIApplicationMain () ser chamado, o que ignorará o AppleLanguages ​​definido anteriormente e exigirá uma reinicialização forçada do aplicativo para ver o idioma desejado.
precisa saber é o seguinte
3
Isso não funciona com itens no plural definidos em Localizable.stringsdict. Alguma idéia se existe uma solução possível?
Mihai Fratu
147

Tive o mesmo problema recentemente e não queria iniciar e corrigir todo o meu NSLocalizedString nem forçar o aplicativo a reiniciar para que o novo idioma funcionasse. Eu queria que tudo funcionasse como está.

Minha solução foi alterar dinamicamente a classe do pacote principal e carregar o pacote apropriado lá:

Arquivo de cabeçalho

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

Implementação

#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

Então, basicamente, quando seu aplicativo iniciar e antes de carregar seu primeiro controlador, basta ligar para:

[NSBundle setLanguage:@"en"];

Quando o usuário alterar o idioma preferido na tela de configurações, basta chamá-lo novamente:

[NSBundle setLanguage:@"fr"];

Para redefinir os padrões do sistema, basta passar nulo:

[NSBundle setLanguage:nil];

Aproveitar...

Para quem precisa de uma versão 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
fonte
7
Oi @Wirsing, isso funciona muito bem para mim até agora. Eu até enviei um aplicativo para a loja e não reclame da Apple.
Gilad Novik
7
Esta é uma ótima solução, também referenciei isso aqui: medium.com/ios-apprentice/905e4052b9de
James Tang
1
Olá @JamesTang, Obrigado pela referência e sua postagem - Encontrei muitas informações úteis sobre localização.
Gilad Novik
3
@ Gilad, seu método funciona perfeito para strings dinâmicas (strings que defini em localizable.strings), mas quanto aos botões e rótulos do storyboard, ele funciona apenas no método principal. então você pode estender sua resposta para incluir a localização de controles da interface do usuário? quero dizer como atualizar (sem fechar) o storyboard depois de chamar [NSBundle setLanguage: @ "??"] ;?
Jawad Al Shaikh
2
Quanto ao XIB / storyboard: você precisa se certificar de chamar esse método ANTES de carregar a visualização. Até a atualização das visualizações ao vivo: no meu caso, simplesmente recarreguei o controlador de exibição (coloquei-o em um controlador de navegação e apenas substituí o controlador. Ao recarregá-lo - ele recebeu as novas strings traduzidas). Estou trabalhando em um cocoapod para isso, provavelmente incluirei um exemplo lá também. Fique atento ...
Gilad Novik 14/05
137

Normalmente faço isso dessa maneira, mas você DEVE ter todos os arquivos de localização no seu projeto.

@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
fonte
1
+1. Esse é um truque muito legal que eu não vi em nenhum outro lugar. Ao criar um "subconjunto" a partir de uma das pastas de localização, você pode fazer com que o material da string funcione bem, desde que envolva o NSLocalizedString com algo que desvie aqui.
Ben Zotto 28/06
4
Excelente Mauro. Notei que ele também pode funcionar para arquivos fora do seu projeto. Se, por algum motivo (como no meu caso), você precisar baixar os arquivos de strings da rede e armazená-los no diretório 'Documents' (com a estrutura de pastas Documents / en.lproj / Localizable.strings, Documents / fr.lproj / Localizable.strings, ...). Você pode fazer um NSBundle. Basta usar este código para o caminho: NSString * path = [NSHomeDirectory () stringByAppendingPathComponent: [NSString stringWithFormat: @ "/ Documents /% @. Lproj", l, nil]];
Arnaud del.
1
Parece-me que esta é a melhor abordagem, juntamente com a contribuição de Alessandro abaixo. Não é tão invasivo e não deve exigir reinicialização.
PKCLsoft
1
O que estou fazendo de errado? Eu criei uma classe Language, herdando o NSObject, coloque isso na implementação, coloque os nomes dos métodos em .h, depois coloque [Language setLanguage: @ "es"]; no meu botão Alterar idioma, o código é executado (o método do botão é chamado e vai para a classe Idioma), mas não faz nada. Também tenho minhas localizable.strings (espanhol). Mas parece estar funcionando para muitas outras pessoas. Por favor, alguém me engane.
18712 SirrupertIII
1
@KKendall, você chama assim: [Language setLanguage: @ "es"]; NSString * stringToUse = [Idioma get: @ "Seu texto" alter: nil];
Mikrasya
41

Não use no iOS 9. Isso retorna nulo para todas as strings passadas por ele.

Encontrei outra solução que permite atualizar as seqüências de idiomas, sem reiniciar o aplicativo e compatível com os geradores:

Coloque essa macro no Prefix.pch:

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

e sempre que você precisar de uma string localizada, use:

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

Para definir o idioma, use:

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

Funciona mesmo com salto de idioma consecutivo, 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
fonte
2
@ powerj1984: não, ele mudará apenas as strings nos seus arquivos de origem. Se você deseja alterar os idiomas do xib, é necessário recarregar os xibs manualmente, a partir do pacote do idioma selecionado.
gklka
@gklka Eu segui os passos dados por Tudozier, mas meus XIBs não estão mudando o idioma, como você disse para recarregar o XIB manualmente, o que realmente significa recarregar XIBs?
Dk Kumar
@DkKumar: Você tem que fazer algo semelhante a isto: stackoverflow.com/questions/21304988/...
gklka
@gklka Sua resposta parece estar correta e eu fiz as mesmas etapas descritas anteriormente, mas o problema é que, se alterarmos o caminho do pacote, ele encontrará todo o recurso (por exemplo, arquivo .png usado no XIB) no diretório mesmo pacote, então temos que copiar a pasta de imagens em todo o pacote, como en.lproj, es.lproj e assim por diante .. então isso afetará o aumento do tamanho do IPA, então existe alguma maneira de superar esse problema? como você disse em seu post, deixe-me tentar sobre isso e deixá-lo mais sobre isso Obrigado por me ajudar para o mesmo
Dk Kumar
1
Isso está horrivelmente quebrado no iOS 9, retornando o nulo para todas as strings.
Alex Zavatone
32

Como dito anteriormente, basta fazer:

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

Mas para evitar ter que reiniciar o aplicativo, coloque a linha no método principal de main.m, pouco antes UIApplicationMain(...).

Frédéric Feytons
fonte
2
Resposta muito útil! O PS pode parecer óbvio para quem não é iniciante, mas você deve inserir essa linha depois NSAutoreleasePool * pool ..ou alguns objetos liberados automaticamente vazarão.
pt2ph8
1
IMPORTANTE: Isso não funcionará se você ligar [[NSBundle mainBundle] URLForResource:withExtension:]antes.
Kof
3
E se você estiver usando o Swift, não haverá main.m?
turingtested
@turingtested você já experimentou rápido e storyboard, está funcionando?
IDevAmit
Oi, Como localizar texto dinâmico que vem do servidor, no meu aplicativo todos os textos que eu tenho que exibir no idioma chinês simplificado, mas como exibir o texto em chinês que vem em inglês. Ajude-me.
Mahesh Narla 28/03
31

O truque para usar um idioma específico, selecionando-o no aplicativo, é forçar o NSLocalizedStringuso de um pacote específico, dependendo do idioma selecionado,

aqui está o post que escrevi para esta localização de avanço de aprendizado em aplicativos iOS

e aqui está o código de uma localização avançada de exemplo de aplicativo em aplicativos iOS

object2.0
fonte
Funciona também no iOS7, tenho que mudar da solução de Brian para isso, pois parece que o iOS substitui a opção de idiomas novamente, por isso fiquei com a primeira configuração do idioma. Sua solução funciona como um encanto!
haxpor
14

O que você acha dessa solução para o 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 simples:

"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
fonte
Oi, Como localizar texto dinâmico que vem do servidor, no meu aplicativo todos os textos que eu tenho que exibir no idioma chinês simplificado, mas como exibir o texto em chinês que vem em inglês. Ajude-me.
Mahesh Narla 28/03
Esta é uma resposta brilhante;)
Bartłomiej Semańczyk
grande resposta. me ajudou
Dilip Tiwari
13

Como Brian Webster menciona, o idioma precisa ser definido "em algum momento no início da inicialização do aplicativo". Pensei applicationDidFinishLaunching:do AppDelegatedeve ser um local adequado para fazê-lo, já que é onde eu faço todos os outros inicialização.

Mas, como William Denniss menciona, isso parece ter efeito apenas depois que o aplicativo é reiniciado, o que é meio inútil.

Parece funcionar bem se eu colocar o código na função principal, no entanto:

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;
}

Eu gostaria de receber comentários sobre isso.

geon
fonte
Na minha aplicação é uma coisa configuráveis pelo usuário - então eu só aparecer uma janela dizendo-lhes que eles vão precisar reiniciar :)
William Denniss
Funciona quase para todos - exceto para a imagem Default.png localizada.
Lukasz
2
Se você estiver permitindo que os usuários definam um idioma e depois instruindo-os a reiniciar o aplicativo, lembre-se de que os padrões NSUserDefault podem não ser salvos se reiniciarem muito rapidamente. A maioria dos usuários não é tão rápida, mas quando você está testando o aplicativo, é muito rápido e vê um comportamento inconsistente como resultado. Levei algumas horas para perceber por que minha mudança de idioma estava funcionando algumas vezes e outras não!
Arlomedia
3
Isso não é problema, basta usar [[NSUserDefaults standardUserDefaults] synchronize];após ligarsetObject:forKey:
Ben Baron
11

Eu gosto do melhor método de Mauro Delrio. Também adicionei o seguinte no meu Project_Prefix.pch

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

Portanto, se você quiser usar o método padrão (que usa NSLocalizedString), poderá fazer uma substituição rápida da sintaxe em todos os arquivos.

dnaxxx
fonte
1
Também gosto do método e adiciono um método para carregar imagens png: + (UIImage ) imageNamed: (NSString *) imageFileName {NSString fullPath = [bundle pathForResource: imageFileName ofType: @ "png"]; UIImage * imageObj = [[UIImage alocado] initWithContentsOfFile: fullPath]; return [autorObjeto imageObj]; }
Jagie
11

NSLocalizedString()lê o valor da chave AppleLanguagesnos padrões padrão do usuário ( [NSUserDefaults standardUserDefaults]). Ele usa esse valor para escolher uma localização apropriada entre todas as localizações existentes no tempo de execução. Quando a Apple cria o dicionário de padrões do usuário no lançamento do aplicativo, eles pesquisam o (s) idioma (s) preferido (s) nas preferências do sistema e copiam o valor a partir daí. Isso também explica, por exemplo, por que a alteração das configurações de idioma no OS X não afeta a execução de aplicativos, apenas nos aplicativos iniciados a partir de então. Uma vez copiado, o valor não é atualizado apenas porque as configurações são alteradas. É por isso que o iOS reinicia todos os aplicativos se você alterar o idioma.

No entanto, todos os valores do dicionário de padrões do usuário podem ser substituídos pelos argumentos da linha de comando. Veja a NSUserDefaultsdocumentação no NSArgumentDomain. Isso inclui até mesmo os valores carregados no arquivo de preferências do aplicativo (.plist). É muito bom saber se você deseja alterar um valor apenas uma vez para o teste .

Portanto, se você deseja alterar o idioma apenas para teste, provavelmente não deseja alterar seu código (se esquecer de removê-lo mais tarde ...), em vez disso, diga ao Xcode para iniciar seu aplicativo com parâmetros de linha de comando ( por exemplo, use a localização em espanhol):

insira a descrição da imagem aqui

Não é necessário tocar no seu código. Basta criar esquemas diferentes para idiomas diferentes e você pode iniciar o aplicativo rapidamente uma vez em um idioma e outra em outro, apenas alternando o esquema.

Mecki
fonte
Funciona apenas pela 1ª vez depois que é carregado novamente no dispositivo pref. language ..
Apr
@Aks Funciona sempre para mim. Certifique-se de estar iniciando o esquema correto, iniciando no Xcode (relançamentos, garfos, etc.), e não substituindo o idioma, Optionscomo nas versões mais recentes do Xcode, que a Apple oferece: também.
Mecki
Obrigado pela sua resposta @Mecki .. Para mim, não está funcionando no xcode, mas quando eu mencionei no código, ele funciona .. Mas ainda assim, se eu defini meu dispositivo com outros idiomas preferidos, ele precisa reiniciar o aplicativo para carregar no meu preferido a linguagem que eu estou passando em argumentos .... #
282
@Aks Ao usá-lo em um dispositivo, ele só funcionará quando iniciado diretamente no Xcode, não funcionará quando você iniciar o aplicativo novamente tocando no ícone de algum dispositivo e também não funcionará quando o aplicativo for morto (por exemplo, porque foi para segundo plano) e é reiniciado pelo sistema (como essa reinicialização não é uma reinicialização feita pelo Xcode) - portanto, alternar para um aplicativo diferente e vice-versa pode não funcionar se o iOS precisar interromper o aplicativo (por exemplo, porque precisa da memória).
Mecki
10

Eu vim com uma solução que permite que você use NSLocalizedString. Eu crio uma categoria de NSBundlechamada NSBundle+RunTimeLanguage. A interface é assim.

// 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

A implementação é assim.

// 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

Do que apenas adicionar importação NSBundle+RunTimeLanguage.hnos arquivos que usam NSLocalizedString.

Como você pode ver, eu armazeno meu languageCode em uma propriedade de AppDelegate. Isso pode ser armazenado em qualquer lugar que você desejar.

A única coisa de que não gosto é um aviso que o NSLocalizedStringmarco redefiniu. Talvez alguém possa me ajudar a consertar essa parte.

qman64
fonte
1
Adicione um #undef NSLocalizedStringpouco antes #definepara desativar o aviso
berílio
Isso funciona na localização do arquivo xib? Eu tenho o arquivo .xib e .strings para traduzir o nome da interface do usuário no idioma específico. Eu tentei e ele não funciona com XIB mas eu não tenho certeza se eu tenho feito as coisas corretamente
Kong Hantrakool
Kong, desculpe pela demora na resposta. Isso funciona onde quer que você use o NSLocalizedString. Portanto, não funcionará diretamente em um XIB. Você precisaria de IBOutlets para as strings no XIB e, em seguida, teria que definir programaticamente os valores de string no código.
Qman64
Essa abordagem funcionou para mim e eu pude alterar os idiomas rapidamente. Observe que códigos de idioma como "es", fr "e" en "funcionaram bem. Mas" zh "(para chinês simplificado) falhou e me devolveu cadeias nulas. Eu fiz uma pesquisa global no meu sistema por" es.lproj "para ver onde os arquivos que eu estava acessando foram e eu descobri que há que "zh.lproj" é realmente "zh-Hans.lproj".
Gallymon
9

Versão rápida:

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

Em poucas palavras:

Localize seu aplicativo

É a primeira coisa que você precisa fazer para localizar seu aplicativo com pelo menos dois idiomas (inglês e francês neste exemplo).

Substituir NSLocalizedString

No seu código, em vez de usar NSLocalizedString(key, comment), use uma macro MYLocalizedString(key, comment)definida assim: #define MYLocalizedString(key, comment) [[MYLocalizationSystem sharedInstance] localizedStringForKey:(key) value:(comment)];

Este MYLocalizationSystemsingleton:

  • Para definir o idioma, defina o usuário NSBundle localizado certo solicita
  • Retorna o NSString localizado de acordo com este idioma definido anteriormente

Definir idioma do usuário

Quando o usuário alterou o idioma do aplicativo em francês, ligue para [[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];
}

Neste exemplo, esse método define o pacote localizado como fr.lproj

Retornar string localizada

Depois de definir o pacote localizado, você poderá obter a string localizada correta com 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 isso ajude você.

Você encontrará mais detalhes neste artigo em NSWinery.io

user3465357
fonte
Pedimos que você não apenas vincule uma solução na sua resposta; o link pode parar de funcionar algum dia. Embora você não precise remover o link da sua resposta, solicitamos que você edite sua resposta para incluir um resumo da seção relevante do artigo vinculado.
Alheio Sábio
7

Swift 3 extensões:

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
fonte
Obrigado. Trabalhando bem.
Krunal Nagvadia
5

No arquivo .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
fonte
Como faço para me livrar do aviso 'ovverride ...'?
Idan Moshe
1
A solução simples faz tudo o que eu preciso. Conseguiu usar isso como um guia para resolver meu problema. Se pathForResource retornar nulo porque não tenho um arquivo de localização, utilizarei pathForResource: @ "Base", pois nada mais do que tentei extrai cadeias de caracteres da localização base. Obrigado.
GRW
Isso é hacky (assim como todas as outras respostas) e foi mais fácil de implementar no teste de unidade que eu estava escrevendo.
Max
4

Talvez você deva complementar isso (no arquivo .pch após #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
fonte
/Users/pwang/Desktop/Myshinesvn/Myshine/viewController/OrientationReadyPagesView.m:193:31: Uso de identificador não declarado 'pacote'
Pengwang
4

Solução 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")

Deu alguns exemplos de códigos de idioma que podem ser usados. Espero que isto ajude

Jeremy Bader
fonte
3

Você pode criar um subconjunto com o conjunto de cadeias localizadas com as quais deseja fazer isso e usá NSLocalizedStringFromTableInBundle()-las para carregá-las. (Suponho que esse conteúdo seja separado da localização normal da interface do usuário que você pode estar fazendo no aplicativo.)

Sixten Otto
fonte
3

para o meu caso, eu tenho dois arquivos localizados, ja e en

e eu gostaria de forçá-lo a en se o idioma preferido no sistema nem en ou ja

vou editar o arquivo main.m

vou verificar se o primeiro preferido é en ou ja; caso contrário, alterarei o segundo idioma preferido para 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
fonte
Obrigado @ chings228, é útil para mim, mas isso não altera a imagem de inicialização padrão (splash) para o aplicativo. Eu tenho diferentes splashs para cada idioma. você sabe como se inscrever para isso?
JERC
Eu acho que o respingo é executado antes do programa principal começar a correr, então eu não sei como a substituí-lo, pode ser plist pode ajudar, mas ele pode trabalhar para a próxima vez que o aplicativo abre
chings228
2

Você pode fazer algo assim:

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
fonte
2

Com base na resposta do Tudorizer para alterar o idioma sem sair ou reiniciar o aplicativo.

Em vez de uma macro, use uma classe para acessar o idioma preferido para verificar se um código de idioma específico está presente.

Abaixo está uma classe usada para obter o pacote de idiomas atual que está funcionando no 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 a classe acima para referenciar um arquivo / imagem / vídeo / 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")

Altere o idioma in-line como abaixo ou atualize o método "changeCurrentLanguage" na classe acima para obter um parâmetro de string referente ao novo idioma.

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

No swift 4, eu o resolvi sem precisar reiniciar ou usar as bibliotecas.

Depois de tentar muitas opções, encontrei esta função, na qual você transmite a stringToLocalize (de Localizable.String, o arquivo de strings) que deseja traduzir e o idioma em que deseja traduzi-lo, e o que ele retorna é o valor para essa String que você possui no arquivo Strings:

    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)
    }

Levando em conta essa função, criei essa função em um arquivo 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 acessar de todo o aplicativo e em cada sequência do restante dos ViewControllers, em vez de colocar:

NSLocalizedString ("StringToLocalize", comment: ")

Eu substituí-o por

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

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

Não sei se é o melhor caminho, mas achei muito simples e funciona para mim, espero que ajude!

ainareta685
fonte
1

Esta função tentará obter uma string localizada para o idioma atual e, se não for encontrada, a obterá no idioma 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
fonte
1

Eu queria adicionar suporte para um idioma que não é oficialmente suportado pelo iOS (não listado na seção Idioma nas configurações do sistema). Seguindo o Tutorial de internacionalização da Apple e algumas dicas aqui de Brian Webster e Geon, vim com esse pedaço de código (coloque-o em 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]));
    }
}

Isso funciona bem para o código do Storyboard e do NSLocalizedString. O código pressupõe que o usuário terá a opção de alterar manualmente o idioma dentro do aplicativo posteriormente.

Obviamente, não esqueça de adicionar traduções apropriadas do Storyboard e traduções Localizable.strings (consulte o link para a página da Apple acima para saber como fazer isso).

frin
fonte
Vejo que você também está enfrentando problemas com o idioma esloveno :) Para melhorar esta resposta para idiomas não suportados pelo iOS, você já descobriu se podemos definir cadeias localizáveis ​​personalizadas que serão usadas para os controles do sistema no aplicativo (Editar, Concluído, Mais, ...)?
miham
1

Aqui está uma solução decente para esse problema e não requer a reinicialização do aplicativo.

https://github.com/cmaftuleac/BundleLocalization

Essa implementação funciona aprimorando o NSBundle. A idéia é que você substitua o método localizedStringForKey na instância do objeto NSBundle e chame esse método em um pacote diferente com um idioma diferente. Simples e elegante totalmente compatível com todos os tipos de recursos.

Corneliu Maftuleac
fonte
Esse código do projeto me ajudou muito. Como encontrar um pacote para código de idioma específico ---NSString *path = [[NSBundle mainBundle] pathForResource:lang ofType:@"lproj" ];
Eonil
Esta classe está funcionando bem, mas depois de alterar o idioma para outro, não está afetando.
Anilkumar iOS ReactNative
0

faça o que fizer, a melhor maneira é usar o short_name para o idioma especificado, ou seja: fr, en, nl, de, it, etc ... e atribuir o mesmo a um valor global.

faça com que uma exibição de selecionador apareça como um menu suspenso (combinação de um botão ao clicar no qual uma exibição de selecionador aparece abaixo com uma lista de idiomas) e selecione o idioma desejado. deixe o nome abreviado ser armazenado internamente. crie um arquivo .h + .m chamado LocalisedString.

Defina o valor global de short_name como igual ao valor obtido em LocalisedString.m Quando o idioma necessário for selecionado, atribua o NSBundlePath para criar subdiretórios do projeto para o idioma necessário. por exemplo, nl.proj, en.proj.

Quando a pasta específica do projetor for selecionada, chame a sequência localizada para o respectivo idioma e altere o idioma dinamicamente.

sem regras quebradas.

Abhijeet
fonte
0

Para o Swift, você pode substituir o main.swiftarquivo e definir a string UserDefaults antes de executar o aplicativo. Dessa forma, você não precisa reiniciar o aplicativo para ver o efeito desejado.

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))

emparelhado com a remoção do @UIApplicationMainseu AppDelegate.swiftarquivo.

Andreas
fonte