Изменение языка на лету, при запуске iOS, программно
Я много часов набирал в штаны и гуглинг. И теперь я отчаянно отчаялся.
Я хотел бы изменить язык моего приложения внутри приложения не только с языком по умолчанию.
Из того, что я пробовал, я застрял как все с шагом перезагрузки. Смысл, яблоки заставляют вас перезапустить приложение вручную. Это означает, что вам нужно выйти из приложения, а затем снова запустить его.
Ну, после googling, я пытался настроить будильник, а затем заставил приложение выйти с помощью
exit(0);
Мне плохо, яблоко, похоже, не нравится и не позволяет разработчику использовать его... Наверное, я не указываю в правильном направлении.
Наконец, несмотря на всю проблему, я мог бы встретиться, я хотел бы обсудить это.
Любые подсказки?
EDIT, информация из APPLE
В общем, вы не должны изменять iOS (с использованием AppleLanguages pref key) изнутри ваше приложение. Это противоречит базовая модель пользователя iOS для переключения языков в приложении "Настройки" и также использует ключ предпочтения, который не является документально, что означает, что в какой-то момент в будущем ключевое имя может изменения, которые приложение.
Если вы хотите переключать языки в ваше приложение, вы можете сделать это через вручную загружать файлы ресурсов в ваш комплект. Вы можете использовать NSBundle: pathForResource: OfType: inDirectory: forLocalization: для этой цели, но имейте в виду что ваше приложение будет ответственный за всю локализованные данные.
Что касается вопроса о выходе (0), Apple DTS не может комментировать утверждение приложения обработать. Вы должны связаться [email protected], чтобы получить ответ для этого вопроса.
Ну, я должен выбрать до сих пор.
Ответы
Ответ 1
Это довольно старый вопрос, но я просто боролся с той же проблемой и нашел это решение:
http://aggressive-mediocrity.blogspot.com/2010/03/custom-localization-system-for-your.html
Что делает именно то, что вам нужно (и может быть полезно для других, у кого с той же проблемой:)
Ответ 2
Ниже ссылка имеет приятную реализацию использования пользовательского языка из приложения в приложении.
ручной выбор языка в iOS-приложении (iPhone и iPad)
Попытка версии SWIFT найти ее здесь
LanguageSettings_Swift
![LanguageChange]()
-anoop
Ответ 3
да, у меня была та же проблема, тогда я справился с ней с помощью своего собственного языка в моем prefFile, где я установил переменную для языка:
// write a new value in file and set the var
- (void)changeLangInPrefFile:(NSString *)newLanguage {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:@"myPreference.plist"];
NSMutableDictionary *data = [[NSMutableDictionary alloc] initWithContentsOfFile: path];
//here add elements to data file and write data to file
[data setObject:newLanguage forKey:@"language"];
[data writeToFile:path atomically:YES];
[data release];
// NSString *chosenLang; <- declared in .h file
if (chosenLang != nil){
[chosenLang release];
chosenLang = nil;
}
chosenLang = [[NSString alloc] initWithString:(@"%@",newLanguage)];
}
// read the language from file and set the var:
- (void)readFromFileInBundleDocuments {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:@"myPreference.plist"];
NSMutableDictionary *savedStock = [[NSMutableDictionary alloc] initWithContentsOfFile:path];
NSString *chosenLangTemp = [savedStock objectForKey:@"language"];
NSLog (@"read in file: %@", chosenLangTemp);
if (chosenLang != nil){
[chosenLang release];
chosenLang = nil;
}
chosenLang = [[NSString alloc] initWithString:(@"%@",chosenLangTemp)];
[savedStock release];
}
тогда я загружаю все содержимое из разных файлов в зависимости от языка
например, я могу загрузить "an_image_eng.png" или "an_image_ita.png",
или имеют 2 разных файла .xib
и для загрузки текста я использую разные словарные файлы, по одному для каждого языка, при переводе всех слов/выражений, я просто загружаю выбранный и читаю в нем правильное выражение для каждого загружаемого текста (код для его загрузки аналогичен методу, который я написал в этом примере, вы можете просто упорядочить его, чтобы прочитать правильное слово для каждого выражения: просто посмотрите на значение objectForKey в правом словаре, где objectForKey - это слово для перевода, а его значение - слово переведено)...
Ответ 4
Как правило, язык, который видит пользователь, определяется настройкой локали, которая является общесистемной настройкой. Только пользователь может изменить его, и когда он это делает, SpringBoard и каждое запущенное приложение на устройстве должны перезапустить. Нет никакого способа обойти это, потому что все системные приложения и фреймворки предполагают, что локаль не меняется после их запуска. Для Apple это очень сложно изменить приложения и фреймворки, чтобы не требовать перезапуска.
Я предполагаю, что вы либо хотите полностью изменить язык интерфейса приложения независимо от настройки языковой системы системы, либо хотите использовать настройку локали системы по умолчанию, но разрешите пользователю переопределять ее только для своего приложения.
Вы можете получить текущую локаль и изучить ее различные значения с помощью +[NSLocale currentLocale]
. Чтобы отобразить пользовательский интерфейс приложения на языке, который не зависит от языкового стандарта системы, вам необходимо полностью исключить использование NSLocalizedString()
и использовать собственное пользовательское состояние, чтобы определить, какие строки отображать пользователю и как изменить интерфейс, чтобы он соответствовал вашему языку приложений. Вам будет необходимо сохранить состояние вашего приложения и соответствующим образом изменить его пользовательский интерфейс.
Ответ 5
В соответствии с Apple guidelines
это не рекомендуется менять язык в приложении программно, но в случае, если у вас нет полномочий изменять запрашиваемое поведение, вы можете сделать что-то вроде следующего:
-
Подготовьте некоторую услугу для управления своим языком даже после перезапуска приложения.
enum LanguageName: String {
case undefined
case en
case es
}
let DynamicLanguageServiceDidDetectLanguageSwitchNotificationKey = "DynamicLanguageServiceDidDetectLanguageSwitchNotificationKey"
func dynamicLocalizableString(_ key: String) -> String {
return LanguageService.service.dynamicLocalizedString(key)
}
class LanguageService {
private struct Defaults {
static let keyAppleLanguage = "AppleLanguages"
static let keyCurrentLanguage = "KeyCurrentLanguage"
}
static let service:LanguageService = LanguageService()
var languageCode: String {
get {
return language.rawValue
}
}
var currentLanguage:LanguageName {
get {
var currentLanguage = UserDefaults.standard.object(forKey: Defaults.keyCurrentLanguage)
if let currentLanguage = currentLanguage as? String {
UserDefaults.standard.set([currentLanguage], forKey: Defaults.keyAppleLanguage)
UserDefaults.standard.synchronize()
} else {
if let languages = UserDefaults.standard.object(forKey: Defaults.keyAppleLanguage) as? [String] {
currentLanguage = languages.first
}
}
if let currentLanguage = currentLanguage as? String,
let lang = LanguageName(rawValue: currentLanguage) {
return lang
}
return LanguageName.undefined
}
}
func switchToLanguage(_ lang:LanguageName) {
language = lang
NotificationCenter.default.post(name: NSNotification.Name(rawValue: DynamicLanguageServiceDidDetectLanguageSwitchNotificationKey), object: nil)
}
private var localeBundle:Bundle?
fileprivate var language: LanguageName = LanguageName.en {
didSet {
let currentLanguage = language.rawValue
UserDefaults.standard.set([currentLanguage], forKey:Defaults.keyAppleLanguage)
UserDefaults.standard.setValue(currentLanguage, forKey:Defaults.keyCurrentLanguage)
UserDefaults.standard.synchronize()
setLocaleWithLanguage(currentLanguage)
}
}
// MARK: - LifeCycle
private init() {
prepareDefaultLocaleBundle()
}
//MARK: - Private
fileprivate func dynamicLocalizedString(_ key: String) -> String {
var localizedString = key
if let bundle = localeBundle {
localizedString = NSLocalizedString(key, bundle: bundle, comment: "")
} else {
localizedString = NSLocalizedString(key, comment: "")
}
return localizedString
}
private func prepareDefaultLocaleBundle() {
var currentLanguage = UserDefaults.standard.object(forKey: Defaults.keyCurrentLanguage)
if let currentLanguage = currentLanguage as? String {
UserDefaults.standard.set([currentLanguage], forKey: Defaults.keyAppleLanguage)
UserDefaults.standard.synchronize()
} else {
if let languages = UserDefaults.standard.object(forKey: Defaults.keyAppleLanguage) as? [String] {
currentLanguage = languages.first
}
}
if let currentLanguage = currentLanguage as? String {
updateCurrentLanguageWithName(currentLanguage)
}
}
private func updateCurrentLanguageWithName(_ languageName: String) {
if let lang = LanguageName(rawValue: languageName) {
language = lang
}
}
private func setLocaleWithLanguage(_ selectedLanguage: String) {
if let pathSelected = Bundle.main.path(forResource: selectedLanguage, ofType: "lproj"),
let bundleSelected = Bundle(path: pathSelected) {
localeBundle = bundleSelected
} else if let pathDefault = Bundle.main.path(forResource: LanguageName.en.rawValue, ofType: "lproj"),
let bundleDefault = Bundle(path: pathDefault) {
localeBundle = bundleDefault
}
}
}
-
Добавьте некоторые правила, чтобы убедиться, что компоненты пользовательского интерфейса будут постоянно обновляться:
protocol Localizable {
func localizeUI()
}
-
Внедрить их
class LocalizableViewController: UIViewController {
// MARK: - LifeCycle
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(self.localizeUI), name: NSNotification.Name(rawValue:DynamicLanguageServiceDidDetectLanguageSwitchNotificationKey), object: nil)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
localizeUI()
}
deinit {
NotificationCenter.default.removeObserver(self)
}
}
extension LocalizableViewController: Localizable {
// MARK: - Localizable
func localizeUI() {
fatalError("Must Override to provide inApp localization functionality")
}
}
-
Наследовать любой контроллер u, чтобы он соответствовал динамическим функциям переключателя приложений и реализовал localizeUI()
func
final class WelcomeTableViewController: LoadableTableViewController
-
Переключить язык по мере необходимости:
LanguageService.service.switchToLanguage(.en)
-
Все строки localizabled должны быть установлены как:
label.text = dynamicLocalizableString(<KEY_IN_STRINGS>)
Примечание. Не забудьте добавить Localizable.strings
с теми же кодами, что и в LanguageName
![введите описание изображения здесь]()
Ответ 6
Это старый вопрос, но я разрабатывал помощника, который уведомляет меня, когда язык меняется "на лету".
Взгляните на код помощника:
import Foundation
class LocalizableLanguage {
// MARK: Constants
fileprivate static let APPLE_LANGUAGE_KEY = "AppleLanguages"
/// Notification Name to observe when language change
static let ApplicationDidChangeLanguage = Notification.Name("ApplicationDidChangeLanguage")
// MARK: Properties
/// An array with all available languages as String
static var availableLanguages: [String]? = {
return UserDefaults.standard.object(forKey: APPLE_LANGUAGE_KEY) as? [String]
}()
/// The first element of available languages that is the current language
static var currentLanguageCode: String? = {
return availableLanguages?.first
}()
/// The current language code with just 2 characters
static var currentShortLanguageCode: String? = {
guard let currentLanguageCode = currentLanguageCode else {
return nil
}
let strIndex = currentLanguageCode.index(currentLanguageCode.startIndex, offsetBy: 2)
return currentLanguageCode.substring(to: strIndex)
}()
// MARK: Handle functions
/// This accepts the short language code or full language code
/// Setting this will send a notification with name "ApplicationDidChangeLanguage", that can be observed in order to refresh your localizable strings
class func setLanguage(withCode langCode: String) {
let matchedLangCode = availableLanguages?.filter {
$0.contains(langCode)
}.first
guard let fullLangCode = matchedLangCode else {
return
}
var reOrderedArray = availableLanguages?.filter {
$0.contains(langCode) == false
}
reOrderedArray?.insert(fullLangCode, at: 0)
guard let langArray = reOrderedArray else {
return
}
UserDefaults.standard.set(langArray, forKey: APPLE_LANGUAGE_KEY)
UserDefaults.standard.synchronize()
LocalizableLanguage.refreshAppBundle()
NotificationCenter.default.post(name: ApplicationDidChangeLanguage, object: fullLangCode)
}
}
// MARK: Refresh Bundle Helper
private extension LocalizableLanguage {
class func refreshAppBundle() {
MethodSwizzleGivenClassName(cls: Bundle.self, originalSelector: #selector(Bundle.localizedString(forKey:value:table:)), overrideSelector: #selector(Bundle.specialLocalizedStringForKey(_:value:table:)))
}
class func MethodSwizzleGivenClassName(cls: AnyClass, originalSelector: Selector, overrideSelector: Selector) {
let origMethod: Method = class_getInstanceMethod(cls, originalSelector);
let overrideMethod: Method = class_getInstanceMethod(cls, overrideSelector);
if (class_addMethod(cls, originalSelector, method_getImplementation(overrideMethod), method_getTypeEncoding(overrideMethod))) {
class_replaceMethod(cls, overrideSelector, method_getImplementation(origMethod), method_getTypeEncoding(origMethod));
} else {
method_exchangeImplementations(origMethod, overrideMethod);
}
}
}
extension Bundle {
func specialLocalizedStringForKey(_ key: String, value: String?, table tableName: String?) -> String {
let availableLanguages = UserDefaults.standard.object(forKey: LocalizableLanguage.APPLE_LANGUAGE_KEY) as? [String]
let currentLanguageCode = availableLanguages?.first ?? "en-US"
let currentShortLanguageCode = currentLanguageCode.substring(to: currentLanguageCode.index(currentLanguageCode.startIndex, offsetBy: 2))
let path =
Bundle.main.path(forResource: currentLanguageCode, ofType: "lproj") ??
Bundle.main.path(forResource: currentShortLanguageCode, ofType: "lproj") ??
Bundle.main.path(forResource: "Base", ofType: "lproj")
guard
self == Bundle.main,
let bundlePath = path,
let bundle = Bundle(path: bundlePath)
else {
return self.specialLocalizedStringForKey(key, value: value, table: tableName)
}
return bundle.specialLocalizedStringForKey(key, value: value, table: tableName)
}
}
Вам просто нужно скопировать этот код и добавить в свой проект.
Затем вы просто реализуете слушателя следующим образом:
NotificationCenter.default.addObserver(forName: LocalizableLanguage.ApplicationDidChangeLanguage, object: nil, queue: nil) { notification in
guard let langCode = notification.object as? String else {
return
}
self.accountStore.languageCode.value = langCode
}
Обратите внимание, что эта строка self.accountStore.languageCode.value = langCode
- это то, что мне нужно обновить при изменении языка приложения, а затем я могу легко изменить все строки моих ViewModels, чтобы немедленно изменить язык для пользователя.
Чтобы изменить язык, вы можете просто позвонить:
LocalizableLanguage.setLanguage(withCode: "en")
Другим помощником, который может быть приятным для вас, является:
import Foundation
extension String {
var localized: String {
return NSLocalizedString(self, comment: "")
}
}
Поэтому, если у вас есть в локализуемых файлах что-то вроде этого:
main.view.title = "Title test";
Вы можете просто позвонить:
"main.view.title".localized
И ваша строка переведена.