我一直在堆叠和谷歌搜索几个小时。我现在有点绝望了。 我想在应用程序中更改应用程序的语言,而不仅仅是使用默认语言。
从我的尝试开始,我就像重启步骤一样陷入困境。意思是,苹果强制您手动重启应用程序。这意味着您必须退出应用程序然后再次启动它。
好吧,谷歌搜索后,我试图设置一个警报,然后强迫应用程序退出
exit(0);
我的不好,苹果似乎不喜欢这样,并阻止开发者使用它...我想我没有指向正确的方向。
最后,尽管存在所有问题,我还是可以见面,我想讨论一下。
任何提示?
编辑,APPLE的信息
一般情况下,你不应该改变 iOS系统语言(通过使用 AppleLanguages pref key)来自内部 你的申请。这违背了 用于切换的基本iOS用户模型 “设置”应用中的语言和 还使用了不是的首选键 记录在案,意思是在某些时候 在未来,关键名称可以 改变,这将打破你的 应用
如果要切换语言 你的申请,你可以通过 手动加载资源文件 你的捆绑。您可以使用 一个NSBundle:pathForResource:ofType:inDirectory:forLocalization: 为此目的,但请记住 你的申请会是 负责所有装载 本地化数据。
关于退出(0)问题,Apple DTS无法评论应用批准 处理。你应该联系 appreview@apple.com得到答案 对于这个问题。
好吧,到目前为止我必须选择。
答案 0 :(得分:21)
这是一个相当古老的问题,但我只是在努力解决同样的问题,并找到了这个解决方案:
http://aggressive-mediocrity.blogspot.com/2010/03/custom-localization-system-for-your.html
这正是您所需要的(对于有同样问题的其他人可能会有所帮助:)
答案 1 :(得分:5)
下面的链接有一个很好的实现,在应用程序中使用自定义语言。
manual language selection in an iOS-App (iPhone and iPad)
尝试SWIFT版本在此处找到它 LanguageSettings_Swift
-anoop
答案 2 :(得分: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是要翻译的单词,其值是单词翻译)...
答案 3 :(得分:1)
通常,用户看到的语言由区域设置决定,区域设置是系统范围的设置。只有用户可以更改它,当他这样做时,SpringBoard和设备上的每个正在运行的应用程序都必须重新启动。没有办法解决这个问题,因为所有系统应用程序和框架都假设一旦开始运行,语言环境就不会改变。将应用程序和框架更改为不需要重新启动对Apple来说非常困难。
我猜您要么完全独立于系统区域设置而改变应用界面的语言,要么默认情况下要使用系统区域设置,但允许用户仅为您的应用覆盖它。
您可以使用+[NSLocale currentLocale]
获取当前区域设置并检查其各种值。要以独立于系统区域设置的语言显示应用程序的用户界面,您需要完全避免使用NSLocalizedString()
,并使用您自己的某种自定义状态来确定要显示的字符串。用户以及如何修改界面以适合您的应用程序的语言。您可以自行保留应用的语言状态并适当修改其用户界面。
答案 4 :(得分:0)
根据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
}
}
}
添加一些规则以确保您的UI组件始终更新:
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")
}
}
继承您想要符合动态应用交换机功能的任何控制器并实现localizeUI()
func
final class WelcomeTableViewController: LoadableTableViewController
根据需要切换语言:
LanguageService.service.switchToLanguage(.en)
所有localizabled字符串都应设置为:
label.text = dynamicLocalizableString(<KEY_IN_STRINGS>)
注意:不要忘记添加Localizable.strings
与LanguageName
答案 5 :(得分:0)
这是一个古老的问题,但是我正在开发一个帮助程序,当即时更改语言时会通知我。
看看帮助程序的代码:
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
您已经翻译了字符串。
答案 6 :(得分:0)
在iOS 13中,用户可以选择特定于应用的语言。如果您确实要提供仅通过应用程序选择语言的功能,则可以提供在应用程序中打开设置以选择语言的功能。 https://developer.apple.com/videos/play/wwdc2019/403/