@@ -11,15 +11,15 @@ @implementation UIApplicationShortcutItem (WMFShortcutItem)

+ (UIApplicationShortcutItem *)wmf_search {
return [[UIApplicationShortcutItem alloc] initWithType:WMFIconShortcutTypeSearch
localizedTitle:MWLocalizedString(@"icon-shortcut-search-title", nil)
localizedTitle:WMFLocalizedStringWithDefaultValue(@"icon-shortcut-search-title", nil, NSBundle.wmf_localizationBundle, @"Search Wikipedia", @"Title for app icon force touch shortcut to quickly open the search interface.")
localizedSubtitle:@""
icon:[UIApplicationShortcutIcon iconWithTemplateImageName:@"search"]
userInfo:nil];
}

+ (UIApplicationShortcutItem *)wmf_random {
return [[UIApplicationShortcutItem alloc] initWithType:WMFIconShortcutTypeRandom
localizedTitle:MWLocalizedString(@"icon-shortcut-random-title", nil)
localizedTitle:WMFLocalizedStringWithDefaultValue(@"icon-shortcut-random-title", nil, NSBundle.wmf_localizationBundle, @"Random article", @"Title for app icon force touch shortcut to quickly open a random article.\n{{Identical|Random article}}")
localizedSubtitle:@""
icon:[UIApplicationShortcutIcon iconWithTemplateImageName:@"random-quick-action"]
userInfo:nil];
@@ -31,15 +31,15 @@ + (nullable UIApplicationShortcutItem *)wmf_continueReading {
return nil;
}
return [[UIApplicationShortcutItem alloc] initWithType:WMFIconShortcutTypeContinueReading
localizedTitle:MWLocalizedString(@"icon-shortcut-continue-reading-title", nil)
localizedTitle:WMFLocalizedStringWithDefaultValue(@"icon-shortcut-continue-reading-title", nil, NSBundle.wmf_localizationBundle, @"Continue reading", @"Title for app icon force touch shortcut to quickly re-open the last article the user was reading.")
localizedSubtitle:lastRead.wmf_title
icon:[UIApplicationShortcutIcon iconWithTemplateImageName:@"home-continue-reading-mini"]
userInfo:nil];
}

+ (UIApplicationShortcutItem *)wmf_nearby {
return [[UIApplicationShortcutItem alloc] initWithType:WMFIconShortcutTypeNearby
localizedTitle:MWLocalizedString(@"icon-shortcut-nearby-title", nil)
localizedTitle:WMFLocalizedStringWithDefaultValue(@"icon-shortcut-nearby-title", nil, NSBundle.wmf_localizationBundle, @"Nearby articles", @"Title for app icon force touch shortcut to quickly open the nearby articles interface.")
localizedSubtitle:@""
icon:[UIApplicationShortcutIcon iconWithTemplateImageName:@"nearby-quick-action"]
userInfo:nil];
@@ -6,7 +6,7 @@ + (UIBarButtonItem *)wmf_buttonType:(WMFButtonType)type target:(nullable id)targ
UIButton *button = [UIButton wmf_buttonType:type target:target action:action];
UIBarButtonItem *item = [[UIBarButtonItem alloc] initWithCustomView:button];
item.width = button.intrinsicContentSize.width;
item.accessibilityLabel = MWLocalizedString(@"close-button-accessibility-label", nil);
item.accessibilityLabel = WMFLocalizedStringWithDefaultValue(@"close-button-accessibility-label", nil, NSBundle.wmf_localizationBundle, @"Close", @"Accessibility label for a button that closes a dialog.\n{{Identical|Close}}");
return item;
}

@@ -4,13 +4,13 @@ extension UIViewController {

func wmf_showAlertWithError(_ error: NSError) {
let alert = UIAlertController(title: error.localizedDescription, message: error.localizedRecoverySuggestion, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: localizedStringForKeyFallingBackOnEnglish("button-ok"), style:.default, handler: nil))
alert.addAction(UIAlertAction(title: WMFLocalizedString("button-ok", value:"OK", comment:"Button text for ok button used in various places\n{{Identical|OK}}"), style:.default, handler: nil))
present(alert, animated: true, completion: nil)
}

func wmf_showAlertWithMessage(_ message: String) {
let alert = UIAlertController(title: nil, message: message, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: localizedStringForKeyFallingBackOnEnglish("button-ok"), style:.default, handler: nil))
alert.addAction(UIAlertAction(title: WMFLocalizedString("button-ok", value:"OK", comment:"Button text for ok button used in various places\n{{Identical|OK}}"), style:.default, handler: nil))
present(alert, animated: true, completion: nil)
}

@@ -19,12 +19,12 @@ - (void)wmf_openExternalUrl:(NSURL *)url useSafari:(BOOL)useSafari {
//If zero rated, don't open any external (non-zero rated!) links until user consents!
if ([SessionSingleton sharedInstance].zeroConfigurationManager.isZeroRated && [[NSUserDefaults wmf_userDefaults] boolForKey:WMFZeroWarnWhenLeaving]) {
WMFZeroConfiguration *zeroConfiguration = [SessionSingleton sharedInstance].zeroConfigurationManager.zeroConfiguration;
NSString *exitDialogTitle = zeroConfiguration.exitTitle ?: MWLocalizedString(@"zero-interstitial-title", nil);
NSString *messageWithHost = [NSString stringWithFormat:@"%@\n\n%@", zeroConfiguration.exitWarning ?: MWLocalizedString(@"zero-interstitial-leave-app", nil), url.host];
NSString *exitDialogTitle = zeroConfiguration.exitTitle ?: WMFLocalizedStringWithDefaultValue(@"zero-interstitial-title", nil, NSBundle.wmf_localizationBundle, @"Leaving Wikipedia Zero", @"Alert text for leaving Wikipedia Zero");
NSString *messageWithHost = [NSString stringWithFormat:@"%@\n\n%@", zeroConfiguration.exitWarning ?: WMFLocalizedStringWithDefaultValue(@"zero-interstitial-leave-app", nil, NSBundle.wmf_localizationBundle, @"Data charges may apply. Continue to external site?", @"Alert text shown if Wikipedia Zero free data access is enabled and user taps external link"), url.host];

UIAlertController *zeroAlert = [UIAlertController alertControllerWithTitle:exitDialogTitle message:messageWithHost preferredStyle:UIAlertControllerStyleAlert];
[zeroAlert addAction:[UIAlertAction actionWithTitle:MWLocalizedString(@"zero-interstitial-cancel", nil) style:UIAlertActionStyleCancel handler:NULL]];
[zeroAlert addAction:[UIAlertAction actionWithTitle:MWLocalizedString(@"zero-interstitial-continue", nil)
[zeroAlert addAction:[UIAlertAction actionWithTitle:WMFLocalizedStringWithDefaultValue(@"zero-interstitial-cancel", nil, NSBundle.wmf_localizationBundle, @"Stay here", @"Button text to not continue to external site.\n{{Identical|Stay here}}") style:UIAlertActionStyleCancel handler:NULL]];
[zeroAlert addAction:[UIAlertAction actionWithTitle:WMFLocalizedStringWithDefaultValue(@"zero-interstitial-continue", nil, NSBundle.wmf_localizationBundle, @"Leave", @"Button text confirming user wants to continue to external site.\n{{Identical|Leave}}")
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *_Nonnull action) {
[self wmf_openExternalUrlModallyIfNeeded:url forceSafari:useSafari];
@@ -20,12 +20,12 @@ - (void)wmf_collapseTablesForArticle:(MWKArticle *)article {
}

- (NSString *)tableCollapsingJavascriptForArticle:(MWKArticle *)article {
NSString *infoBoxTitle = [WMFLocalizedStringWithDefaultValue(@"info-box-title", article.url, NSBundle.wmf_localizationBundle, @"Quick Facts", @"The title of infoboxes – in collapsed and expanded form") wmf_stringByReplacingApostrophesWithBackslashApostrophes];
NSString *tableTitle = [WMFLocalizedStringWithDefaultValue(@"table-title-other", article.url, NSBundle.wmf_localizationBundle, @"More information", @"The title of non-info box tables - in collapsed and expanded form\n{{Identical|More information}}") wmf_stringByReplacingApostrophesWithBackslashApostrophes];
NSString *closeBoxText = [WMFLocalizedStringWithDefaultValue(@"info-box-close-text", article.url, NSBundle.wmf_localizationBundle, @"Close", @"The text for telling users they can tap the bottom of the info box to close it\n{{Identical|Close}}") wmf_stringByReplacingApostrophesWithBackslashApostrophes];
return
[NSString stringWithFormat:@"window.wmf.tables.hideTables(document, %d, '%@', '%@', '%@');",
article.isMain,
[article apostropheEscapedArticleLanguageLocalizedStringForKey:@"info-box-title"],
[article apostropheEscapedArticleLanguageLocalizedStringForKey:@"table-title-other"],
[article apostropheEscapedArticleLanguageLocalizedStringForKey:@"info-box-close-text"]];
article.isMain, infoBoxTitle, tableTitle, closeBoxText];
}

- (void)wmf_setLanguage:(MWLanguageInfo *)languageInfo {
@@ -20,14 +20,16 @@ import Foundation
return "window.wmf.footerMenu.IconTypeEnum.\(footerMenuJSTransformEnumString)"
}

private var localizedTitleKey: String {
private func localizedTitle(with article: MWKArticle) -> String {
var title = ""
switch self {
case .languages: return "page-read-in-other-languages"
case .lastEdited: return "page-last-edited"
case .pageIssues: return "page-issues"
case .disambiguation: return "page-similar-titles"
case .coordinate: return "page-location"
case .languages: title = WMFLocalizedStringWithDefaultValue("page-read-in-other-languages", article.url, Bundle.wmf_localization, "Available in %1$@ other languages", "Label for button showing number of languages an article is available in. %1$@ will be replaced with the number of languages")
case .lastEdited: title = WMFLocalizedStringWithDefaultValue("page-last-edited", article.url, Bundle.wmf_localization, "Edited %1$@ days ago", "Label for button showing number of days since an article was last edited. %1$@ will be replaced with the number of days")
case .pageIssues: title = WMFLocalizedStringWithDefaultValue("page-issues", article.url, Bundle.wmf_localization, "Page issues", "Label for the button that shows the \"Page issues\" dialog, where information about the imperfections of the current page is provided (by displaying the warning/cleanup templates).\n{{Identical|Page issue}}")
case .disambiguation: title = WMFLocalizedStringWithDefaultValue("page-similar-titles", article.url, Bundle.wmf_localization, "Similar pages", "Label for button that shows a list of similar titles (disambiguation) for the current page")
case .coordinate: title = WMFLocalizedStringWithDefaultValue("page-location", article.url, Bundle.wmf_localization, "View on a map", "Label for button used to show an article on the map")
}
return title.wmf_stringByReplacingApostrophesWithBackslashApostrophes()
}

private func titleSubstitutionStringForArticle(article: MWKArticle) -> String? {
@@ -43,14 +45,15 @@ import Foundation
}
}

private var localizedSubtitleKey: String? {
private func localizedSubtitle(with article: MWKArticle) -> String {
switch self {
case .lastEdited: return "page-edit-history"
case .lastEdited: return WMFLocalizedStringWithDefaultValue("page-edit-history", article.url, Bundle.wmf_localization, "Full edit history", "Label for button used to show an article's complete edit history").wmf_stringByReplacingApostrophesWithBackslashApostrophes()
default:
return nil
return ""
}
}


public func shouldAddItem(with article: MWKArticle) -> Bool {
switch self {
case .languages where !article.hasMultipleLanguages:
@@ -72,15 +75,12 @@ import Foundation
}

public func itemAdditionJavascriptString(with article: MWKArticle) -> String {
var title = article.apostropheEscapedArticleLanguageLocalizedStringForKey(self.localizedTitleKey)
var title = self.localizedTitle(with: article)
if let substitutionString = titleSubstitutionStringForArticle(article: article) {
title = title.replacingOccurrences(of: "$1", with: substitutionString)
title = String.localizedStringWithFormat(title, substitutionString)
}

var subtitle = ""
if let subtitleKey = self.localizedSubtitleKey{
subtitle = article.apostropheEscapedArticleLanguageLocalizedStringForKey(subtitleKey)
}
let subtitle = self.localizedSubtitle(with: article)

let itemSelectionHandler =
"function(){" +
@@ -94,7 +94,7 @@ import Foundation
extension WKWebView {

public func wmf_addFooterMenuForArticle(_ article: MWKArticle){
let heading = article.apostropheEscapedArticleLanguageLocalizedStringForKey("article-about-title").uppercased(with: Locale.current)
let heading = WMFLocalizedStringWithDefaultValue("article-about-title", article.url, Bundle.wmf_localization, "About this article", "The text that is displayed before the 'about' section at the bottom of an article").wmf_stringByReplacingApostrophesWithBackslashApostrophes().uppercased(with: Locale.current)
evaluateJavaScript("window.wmf.footerMenu.setHeading('\(heading)', 'footer_container_menu_heading');", completionHandler: nil)

let itemsJS = [
@@ -111,8 +111,8 @@ extension WKWebView {
}

public func wmf_addFooterLegalForArticle(_ article: MWKArticle){
let licenseString = article.apostropheEscapedArticleLanguageLocalizedStringForKey("license-footer-text")
let licenseSubstitutionString = article.apostropheEscapedArticleLanguageLocalizedStringForKey("license-footer-name")
let licenseString = String.localizedStringWithFormat(WMFLocalizedStringWithDefaultValue("license-footer-text", article.url, Bundle.wmf_localization, "Content is available under %1$@ unless otherwise noted.", "Marker at page end for who last modified the page when anonymous. %1$@ is a relative date such as '2 months ago' or 'today'."), "$1").wmf_stringByReplacingApostrophesWithBackslashApostrophes() // Replace with $1 for JavaScript
let licenseSubstitutionString = WMFLocalizedStringWithDefaultValue("license-footer-name", article.url, Bundle.wmf_localization, "CC BY-SA 3.0", "License short name; usually leave untranslated as CC-BY-SA 3.0\n{{Identical|CC BY-SA}}").wmf_stringByReplacingApostrophesWithBackslashApostrophes()
let licenseLinkClickHandler =
"function(){" +
"window.webkit.messageHandlers.footerLegalLicenseLinkClicked.postMessage('linkClicked');" +
@@ -129,11 +129,11 @@ extension WKWebView {
return
}

let heading = article.apostropheEscapedArticleLanguageLocalizedStringForKey("article-read-more-title").uppercased(with: Locale.current)
let heading = WMFLocalizedStringWithDefaultValue("article-read-more-title", article.url, Bundle.wmf_localization, "Read more", "The text that is displayed before the read more section at the bottom of an article\n{{Identical|Read more}}").wmf_stringByReplacingApostrophesWithBackslashApostrophes().uppercased(with: Locale.current)
evaluateJavaScript("window.wmf.footerReadMore.setHeading('\(heading)', 'footer_container_readmore_heading');", completionHandler: nil)

let saveForLaterString = article.apostropheEscapedArticleLanguageLocalizedStringForKey("button-save-for-later")
let savedForLaterString = article.apostropheEscapedArticleLanguageLocalizedStringForKey("button-saved-for-later")
let saveForLaterString = WMFLocalizedStringWithDefaultValue("button-save-for-later", article.url, Bundle.wmf_localization, "Save for later", "Longer button text for save button used in various places.").wmf_stringByReplacingApostrophesWithBackslashApostrophes()
let savedForLaterString = WMFLocalizedStringWithDefaultValue("button-saved-for-later", article.url, Bundle.wmf_localization, "Saved for later", "Longer button text for already saved button used in various places.").wmf_stringByReplacingApostrophesWithBackslashApostrophes()

let tapHandler =
"function(href){" +
@@ -56,29 +56,29 @@ class WMFAccountCreationViewController: WMFScrollViewController, WMFCaptchaViewC

navigationItem.leftBarButtonItem = UIBarButtonItem(image: UIImage(named:"close"), style: .plain, target:self, action:#selector(closeButtonPushed(_:)))

createAccountButton.setTitle(localizedStringForKeyFallingBackOnEnglish("account-creation-create-account"), for: .normal)
createAccountButton.setTitle(WMFLocalizedString("account-creation-create-account", value:"Create your account", comment:"Text for create account button"), for: .normal)

scrollView.delegate = self

usernameField.placeholder = localizedStringForKeyFallingBackOnEnglish("field-username-placeholder")
passwordField.placeholder = localizedStringForKeyFallingBackOnEnglish("field-password-placeholder")
passwordRepeatField.placeholder = localizedStringForKeyFallingBackOnEnglish("field-password-confirm-placeholder")
emailField.placeholder = localizedStringForKeyFallingBackOnEnglish("field-email-placeholder")
usernameTitleLabel.text = localizedStringForKeyFallingBackOnEnglish("field-username-title")
passwordTitleLabel.text = localizedStringForKeyFallingBackOnEnglish("field-password-title")
passwordRepeatTitleLabel.text = localizedStringForKeyFallingBackOnEnglish("field-password-confirm-title")
emailTitleLabel.text = localizedStringForKeyFallingBackOnEnglish("field-email-title-optional")
passwordRepeatAlertLabel.text = localizedStringForKeyFallingBackOnEnglish("field-alert-password-confirm-mismatch")
usernameField.placeholder = WMFLocalizedString("field-username-placeholder", value:"enter username", comment:"Placeholder text shown inside username field until user taps on it")
passwordField.placeholder = WMFLocalizedString("field-password-placeholder", value:"enter password", comment:"Placeholder text shown inside password field until user taps on it")
passwordRepeatField.placeholder = WMFLocalizedString("field-password-confirm-placeholder", value:"re-enter password", comment:"Placeholder text shown inside confirm password field until user taps on it")
emailField.placeholder = WMFLocalizedString("field-email-placeholder", value:"example@example.org", comment:"Placeholder text shown inside email address field until user taps on it")
usernameTitleLabel.text = WMFLocalizedString("field-username-title", value:"Username", comment:"Title for username field\n{{Identical|Username}}")
passwordTitleLabel.text = WMFLocalizedString("field-password-title", value:"Password", comment:"Title for password field\n{{Identical|Password}}")
passwordRepeatTitleLabel.text = WMFLocalizedString("field-password-confirm-title", value:"Confirm password", comment:"Title for confirm password field")
emailTitleLabel.text = WMFLocalizedString("field-email-title-optional", value:"Email (optional)", comment:"Noun. Title for optional email address field.")
passwordRepeatAlertLabel.text = WMFLocalizedString("field-alert-password-confirm-mismatch", value:"Passwords do not match", comment:"Alert shown if password confirmation did not match password")

usernameField.wmf_addThinBottomBorder()
passwordField.wmf_addThinBottomBorder()
passwordRepeatField.wmf_addThinBottomBorder()
emailField.wmf_addThinBottomBorder()

loginButton.strings = WMFAuthLinkLabelStrings(dollarSignString: localizedStringForKeyFallingBackOnEnglish("account-creation-have-account"), substitutionString: localizedStringForKeyFallingBackOnEnglish("account-creation-log-in"))
loginButton.strings = WMFAuthLinkLabelStrings(dollarSignString: WMFLocalizedString("account-creation-have-account", value:"Already have an account? %1$@", comment:"Text for button which shows login interface. %1$@ is the message {{msg-wikimedia|account-creation-log-in}}"), substitutionString: WMFLocalizedString("account-creation-log-in", value:"Log in.", comment:"Log in text to be used as part of a log in button\n{{Identical|Log in}}"))

loginButton.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(loginButtonPushed(_:))))
titleLabel.text = localizedStringForKeyFallingBackOnEnglish("account-creation-title")
titleLabel.text = WMFLocalizedString("account-creation-title", value:"Create a new account", comment:"Title for account creation interface")

view.wmf_configureSubviewsForDynamicType()

@@ -197,16 +197,16 @@ class WMFAccountCreationViewController: WMFScrollViewController, WMFCaptchaViewC
}

fileprivate func login() {
WMFAlertManager.sharedInstance.showAlert(localizedStringForKeyFallingBackOnEnglish("account-creation-logging-in"), sticky: true, dismissPreviousAlerts: true, tapCallBack: nil)
WMFAlertManager.sharedInstance.showAlert(WMFLocalizedString("account-creation-logging-in", value:"Logging in...", comment:"Alert shown after account successfully created and the user is being logged in automatically.\n{{Identical|Logging in}}"), sticky: true, dismissPreviousAlerts: true, tapCallBack: nil)
WMFAuthenticationManager.sharedInstance.login(
username: usernameField.text!,
password: passwordField.text!,
username: usernameField.text ?? "",
password: passwordField.text ?? "",
retypePassword: nil,
oathToken: nil,
captchaID: nil,
captchaWord: nil,
success: { _ in
let loggedInMessage = localizedStringForKeyFallingBackOnEnglish("main-menu-account-title-logged-in").replacingOccurrences(of: "$1", with: self.usernameField.text!)
let loggedInMessage = String.localizedStringWithFormat(WMFLocalizedString("main-menu-account-title-logged-in", value:"Logged in as %1$@", comment:"Header text used when account is logged in. %1$@ will be replaced with current username."), self.usernameField.text ?? "")
WMFAlertManager.sharedInstance.showSuccessAlert(loggedInMessage, sticky: false, dismissPreviousAlerts: true, tapCallBack: nil)
self.dismiss(animated: true, completion: nil)
}, failure: { error in
@@ -234,15 +234,15 @@ class WMFAccountCreationViewController: WMFScrollViewController, WMFCaptchaViewC
passwordRepeatAlertLabel.isHidden = true

guard areRequiredFieldsPopulated() else {
WMFAlertManager.sharedInstance.showErrorAlertWithMessage(localizedStringForKeyFallingBackOnEnglish("account-creation-missing-fields"), sticky: true, dismissPreviousAlerts: true, tapCallBack: nil)
WMFAlertManager.sharedInstance.showErrorAlertWithMessage(WMFLocalizedString("account-creation-missing-fields", value:"You must enter a username, password, and password confirmation to create an account.", comment:"Error shown when one of the required fields for account creation (username, password, and password confirmation) is empty."), sticky: true, dismissPreviousAlerts: true, tapCallBack: nil)
return
}

guard passwordFieldsMatch() else {
self.passwordRepeatField.textColor = .wmf_yellow
self.passwordRepeatAlertLabel.isHidden = false
self.scrollView.scrollSubView(toTop: self.passwordTitleLabel, offset: 6, animated: true)
WMFAlertManager.sharedInstance.showErrorAlertWithMessage(localizedStringForKeyFallingBackOnEnglish("account-creation-passwords-mismatched"), sticky: false, dismissPreviousAlerts: true, tapCallBack: nil)
WMFAlertManager.sharedInstance.showErrorAlertWithMessage(WMFLocalizedString("account-creation-passwords-mismatched", value:"Password fields do not match.", comment:"Alert shown if the user doesn't enter the same password in both password boxes"), sticky: false, dismissPreviousAlerts: true, tapCallBack: nil)
return
}
wmf_hideKeyboard()
@@ -262,7 +262,7 @@ class WMFAccountCreationViewController: WMFScrollViewController, WMFCaptchaViewC
}

fileprivate func createAccount() {
WMFAlertManager.sharedInstance.showAlert(localizedStringForKeyFallingBackOnEnglish("account-creation-saving"), sticky: true, dismissPreviousAlerts: true, tapCallBack: nil)
WMFAlertManager.sharedInstance.showAlert(WMFLocalizedString("account-creation-saving", value:"Saving...", comment:"Alert shown when user saves account creation form.\n{{Identical|Saving}}"), sticky: true, dismissPreviousAlerts: true, tapCallBack: nil)

let creationFailure: WMFErrorHandler = {error in
// Captcha's appear to be one-time, so always try to get a new one on failure.
@@ -11,9 +11,9 @@ public enum WMFAccountCreatorError: LocalizedError {
case .statusNotPass(let message?):
return message
case .wrongCaptcha:
return localizedStringForKeyFallingBackOnEnglish("field-alert-captcha-invalid")
return WMFLocalizedString("field-alert-captcha-invalid", value:"Invalid CAPTCHA", comment:"Alert shown if CAPTCHA is not correct")
case .usernameUnavailable:
return localizedStringForKeyFallingBackOnEnglish("field-alert-username-unavailable")
return WMFLocalizedString("field-alert-username-unavailable", value:"Username not available", comment:"Alert shown if new username is not available")
default:
return "Unable to create account: Reason unknown"
}
@@ -17,9 +17,9 @@ public enum WMFAccountLoginError: LocalizedError {
case .needsOathTokenFor2FA(let message?):
return message
case .wrongPassword:
return localizedStringForKeyFallingBackOnEnglish("field-alert-password-invalid")
return WMFLocalizedString("field-alert-password-invalid", value:"Invalid password", comment:"Alert shown if password is not correct")
case .wrongToken:
return localizedStringForKeyFallingBackOnEnglish("field-alert-token-invalid")
return WMFLocalizedString("field-alert-token-invalid", value:"Invalid code", comment:"Alert shown if token is not correct")
default:
return "Unable to login: Reason unknown"
}
@@ -5,7 +5,7 @@ extension NSError {

public func alertMessage() -> String {
if(self.wmf_isNetworkConnectionError()){
return localizedStringForKeyFallingBackOnEnglish("alert-no-internet")
return WMFLocalizedString("alert-no-internet", value:"There's no internet connection", comment:"Message shown in an alert banner when there is no connection to the internet.")
}else{
return self.localizedDescription
}
@@ -40,7 +40,7 @@ open class WMFAlertManager: NSObject, TSMessageViewProtocol, MFMailComposeViewCo
}
self.showAlert(dismissPreviousAlerts, alertBlock: { () -> Void in
TSMessage.showNotification(in: nil,
title: localizedStringForKeyFallingBackOnEnglish("in-the-news-title"),
title: WMFLocalizedString("in-the-news-title", value:"In the news", comment:"Title for the 'In the news' notification & feed section"),
subtitle: message,
image: UIImage(named:"trending-notification-icon"),
type: .message,
@@ -173,7 +173,7 @@ open class WMFAlertManager: NSObject, TSMessageViewProtocol, MFMailComposeViewCo
}

open func showEmailFeedbackAlertViewWithError(_ error: NSError) {
let message = localizedStringForKeyFallingBackOnEnglish("request-feedback-on-error")
let message = WMFLocalizedString("request-feedback-on-error", value:"The app has encountered a problem that our developers would like to know more about. Please tap here to send us an email with the error details.", comment:"Displayed to beta users when they encounter an error we'd like feedback on")
showErrorAlertWithMessage(message, sticky: true, dismissPreviousAlerts: true) {
self.dismissAllAlerts()
if MFMailComposeViewController.canSendMail() {
@@ -187,7 +187,7 @@ open class WMFAlertManager: NSObject, TSMessageViewProtocol, MFMailComposeViewCo
vc.setMessageBody("Domain:\t\(error.domain)\nCode:\t\(error.code)\nDescription:\t\(error.localizedDescription)\n\n\n\nVersion:\t\(WikipediaAppUtils.versionedUserAgent())", isHTML: false)
rootVC.present(vc, animated: true, completion: nil)
} else {
self.showErrorAlertWithMessage(localizedStringForKeyFallingBackOnEnglish("no-email-account-alert"), sticky: false, dismissPreviousAlerts: false) {
self.showErrorAlertWithMessage(WMFLocalizedString("no-email-account-alert", value:"Please setup an email account on your device and try again.", comment:"Displayed to the user when they try to send a feedback email, but they have never set up an account on their device"), sticky: false, dismissPreviousAlerts: false) {

}
}
@@ -25,7 +25,7 @@ - (void)awakeFromNib {
self.actionButton.layer.borderColor = self.actionButton.tintColor.CGColor;
[self.actionButton addTarget:self action:@selector(performAction) forControlEvents:UIControlEventTouchUpInside];
self.captionTextView.textContainerInset = UIEdgeInsetsZero;
[self.dismissButton setTitle:MWLocalizedString(@"announcements-dismiss", nil) forState:UIControlStateNormal];
[self.dismissButton setTitle:WMFLocalizedStringWithDefaultValue(@"announcements-dismiss", nil, NSBundle.wmf_localizationBundle, @"No thanks", @"Button text indicating a user wants to dismiss an announcement\n{{Identical|No thanks}}") forState:UIControlStateNormal];
[self.dismissButton addTarget:self action:@selector(dismiss) forControlEvents:UIControlEventTouchUpInside];
[self.dismissButton setNeedsLayout];
[self.dismissButton layoutIfNeeded];
@@ -1102,15 +1102,15 @@ - (BOOL)tabBarController:(UITabBarController *)tabBarController shouldSelectView
- (void)updateActiveTitleAccessibilityButton:(UIViewController *)viewController {
if ([viewController isKindOfClass:[WMFExploreViewController class]]) {
WMFExploreViewController *vc = (WMFExploreViewController *)viewController;
vc.titleButton.accessibilityLabel = MWLocalizedString(@"home-title-accessibility-label", nil);
vc.titleButton.accessibilityLabel = WMFLocalizedStringWithDefaultValue(@"home-title-accessibility-label", nil, NSBundle.wmf_localizationBundle, @"Wikipedia, scroll to top of Explore", @"Accessibility heading for the Explore page, indicating that tapping it will scroll to the top of the explore page. \"Explore\" is the same as {{msg-wikimedia|Wikipedia-ios-welcome-explore-title}}.");
} else if ([viewController isKindOfClass:[WMFArticleViewController class]]) {
WMFArticleViewController *vc = (WMFArticleViewController *)viewController;
if (self.rootTabBarController.selectedIndex == WMFAppTabTypeExplore) {
vc.titleButton.accessibilityLabel = MWLocalizedString(@"home-button-explore-accessibility-label", nil);
vc.titleButton.accessibilityLabel = WMFLocalizedStringWithDefaultValue(@"home-button-explore-accessibility-label", nil, NSBundle.wmf_localizationBundle, @"Wikipedia, return to Explore", @"Accessibility heading for articles shown within the explore tab, indicating that tapping it will take you back to explore. \"Explore\" is the same as {{msg-wikimedia|Wikipedia-ios-welcome-explore-title}}.");
} else if (self.rootTabBarController.selectedIndex == WMFAppTabTypeSaved) {
vc.titleButton.accessibilityLabel = MWLocalizedString(@"home-button-saved-accessibility-label", nil);
vc.titleButton.accessibilityLabel = WMFLocalizedStringWithDefaultValue(@"home-button-saved-accessibility-label", nil, NSBundle.wmf_localizationBundle, @"Wikipedia, return to Saved", @"Accessibility heading for articles shown within the saved articles tab, indicating that tapping it will take you back to the list of saved articles. \"Saved\" is the same as {{msg-wikimedia|Wikipedia-ios-saved-title}}.");
} else if (self.rootTabBarController.selectedIndex == WMFAppTabTypeRecent) {
vc.titleButton.accessibilityLabel = MWLocalizedString(@"home-button-history-accessibility-label", nil);
vc.titleButton.accessibilityLabel = WMFLocalizedStringWithDefaultValue(@"home-button-history-accessibility-label", nil, NSBundle.wmf_localizationBundle, @"Wikipedia, return to History", @"Accessibility heading for articles shown within the history articles tab, indicating that tapping it will take you back to the history list. \"History\" is the same as {{msg-wikimedia|Wikipedia-ios-history-title}}.");
}
}
}
@@ -1173,13 +1173,13 @@ - (void)showFirstTimeZeroOnAlertIfNeeded:(WMFZeroConfiguration *)zeroConfigurati

[self setZeroOnDialogShownOnce];

NSString *title = zeroConfiguration.message ? zeroConfiguration.message : MWLocalizedString(@"zero-free-verbiage", nil);
NSString *title = zeroConfiguration.message ? zeroConfiguration.message : WMFLocalizedStringWithDefaultValue(@"zero-free-verbiage", nil, NSBundle.wmf_localizationBundle, @"Free Wikipedia access from your mobile operator (data charges waived)", @"Alert text for Wikipedia Zero free data access enabled");

UIAlertController *dialog = [UIAlertController alertControllerWithTitle:title message:MWLocalizedString(@"zero-learn-more", nil) preferredStyle:UIAlertControllerStyleAlert];
UIAlertController *dialog = [UIAlertController alertControllerWithTitle:title message:WMFLocalizedStringWithDefaultValue(@"zero-learn-more", nil, NSBundle.wmf_localizationBundle, @"Data charges are waived for this Wikipedia app.", @"Alert text for learning more about Wikipedia Zero") preferredStyle:UIAlertControllerStyleAlert];

[dialog addAction:[UIAlertAction actionWithTitle:MWLocalizedString(@"zero-learn-more-no-thanks", nil) style:UIAlertActionStyleCancel handler:NULL]];
[dialog addAction:[UIAlertAction actionWithTitle:WMFLocalizedStringWithDefaultValue(@"zero-learn-more-no-thanks", nil, NSBundle.wmf_localizationBundle, @"Dismiss", @"Button text for declining to learn more about Wikipedia Zero.\n{{Identical|Dismiss}}") style:UIAlertActionStyleCancel handler:NULL]];

[dialog addAction:[UIAlertAction actionWithTitle:MWLocalizedString(@"zero-learn-more-learn-more", nil)
[dialog addAction:[UIAlertAction actionWithTitle:WMFLocalizedStringWithDefaultValue(@"zero-learn-more-learn-more", nil, NSBundle.wmf_localizationBundle, @"Read more", @"Button text for learn more about Wikipedia Zero.\n{{Identical|Read more}}")
style:UIAlertActionStyleDestructive
handler:^(UIAlertAction *_Nonnull action) {
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"https://m.wikimediafoundation.org/wiki/Wikipedia_Zero_App_FAQ"]];
@@ -1190,9 +1190,9 @@ - (void)showFirstTimeZeroOnAlertIfNeeded:(WMFZeroConfiguration *)zeroConfigurati

- (void)showZeroOffAlert {

UIAlertController *dialog = [UIAlertController alertControllerWithTitle:MWLocalizedString(@"zero-charged-verbiage", nil) message:MWLocalizedString(@"zero-charged-verbiage-extended", nil) preferredStyle:UIAlertControllerStyleAlert];
UIAlertController *dialog = [UIAlertController alertControllerWithTitle:WMFLocalizedStringWithDefaultValue(@"zero-charged-verbiage", nil, NSBundle.wmf_localizationBundle, @"Wikipedia Zero is off", @"Alert text for Wikipedia Zero free data access disabled") message:WMFLocalizedStringWithDefaultValue(@"zero-charged-verbiage-extended", nil, NSBundle.wmf_localizationBundle, @"Loading other articles may incur data charges. Saved articles stored offline do not use data and are free.", @"Extended text describing that further usage of the app may in fact incur data charges because Wikipedia Zero is off, but Saved articles are still free.") preferredStyle:UIAlertControllerStyleAlert];

[dialog addAction:[UIAlertAction actionWithTitle:MWLocalizedString(@"zero-learn-more-no-thanks", nil) style:UIAlertActionStyleCancel handler:NULL]];
[dialog addAction:[UIAlertAction actionWithTitle:WMFLocalizedStringWithDefaultValue(@"zero-learn-more-no-thanks", nil, NSBundle.wmf_localizationBundle, @"Dismiss", @"Button text for declining to learn more about Wikipedia Zero.\n{{Identical|Dismiss}}") style:UIAlertActionStyleCancel handler:NULL]];

[[[[UIApplication sharedApplication] keyWindow] rootViewController] presentViewController:dialog animated:YES completion:NULL];
}
@@ -107,7 +107,8 @@ extension WMFArticleViewController {
*/
public func createTableOfContentsViewControllerIfNeeded() {
if let items = createTableOfContentsSections() {
self.tableOfContentsViewController = WMFTableOfContentsViewController(presentingViewController: tableOfContentsDisplayMode == .modal ? self : nil , items: items, delegate: self)
let semanticContentAttribute:UISemanticContentAttribute = MWLanguageInfo.articleLanguageIsRTL(article) ? .forceRightToLeft : .forceLeftToRight
self.tableOfContentsViewController = WMFTableOfContentsViewController(presentingViewController: tableOfContentsDisplayMode == .modal ? self : nil , items: items, delegate: self, semanticContentAttribute: semanticContentAttribute)
}
}

@@ -21,7 +21,7 @@ - (void)awakeFromNib {
backgroundView.backgroundColor = [UIColor wmf_settingsBackground];
self.backgroundView = backgroundView;
self.titleLabel.textColor = [UIColor wmf_777777];
[self.addButton setTitle:MWLocalizedString(@"welcome-languages-add-button", nil)
[self.addButton setTitle:WMFLocalizedStringWithDefaultValue(@"welcome-languages-add-button", nil, NSBundle.wmf_localizationBundle, @"Add another language", @"Title for button for adding another language")
forState:UIControlStateNormal];
[self.addButton setTitleColor:[UIColor wmf_blueTint] forState:UIControlStateNormal];
[self wmf_configureSubviewsForDynamicType];
@@ -12,7 +12,7 @@ - (void)awakeFromNib {
self.placeholderImageView.image = stretch;
self.placeholderImageView.contentMode = UIViewContentModeScaleToFill;
[self.placeholderSaveButton setImage:[UIImage imageNamed:@"save-mini"] forState:UIControlStateNormal];
[self.placeholderSaveButton setTitle:MWLocalizedString(@"button-save-for-later", nil) forState:UIControlStateNormal];
[self.placeholderSaveButton setTitle:WMFLocalizedStringWithDefaultValue(@"button-save-for-later", nil, NSBundle.wmf_localizationBundle, @"Save for later", @"Longer button text for save button used in various places.") forState:UIControlStateNormal];
self.placeholderSaveButton.tintColor = [UIColor wmf_placeholderLightGray];
}

@@ -12,7 +12,7 @@ - (void)awakeFromNib {
self.placeholderImageView.image = stretch;
self.placeholderImageView.contentMode = UIViewContentModeScaleToFill;
[self.placeholderSaveButton setImage:[UIImage imageNamed:@"save-mini"] forState:UIControlStateNormal];
[self.placeholderSaveButton setTitle:MWLocalizedString(@"button-save-for-later", nil) forState:UIControlStateNormal];
[self.placeholderSaveButton setTitle:WMFLocalizedStringWithDefaultValue(@"button-save-for-later", nil, NSBundle.wmf_localizationBundle, @"Save for later", @"Longer button text for save button used in various places.") forState:UIControlStateNormal];
self.placeholderSaveButton.tintColor = [UIColor wmf_placeholderLightGray];
}

@@ -115,7 +115,7 @@ - (NSUInteger)titleCount {
}

- (nullable NSString *)displayTitle {
return MWLocalizedString(@"page-similar-titles", nil);
return WMFLocalizedStringWithDefaultValue(@"page-similar-titles", nil, NSBundle.wmf_localizationBundle, @"Similar pages", @"Label for button that shows a list of similar titles (disambiguation) for the current page");
}

- (BOOL)canDeleteItemAtIndexpath:(NSIndexPath *__nonnull)indexPath {
@@ -34,7 +34,7 @@ - (nullable id)activityViewController:(UIActivityViewController *)activityViewCo
}
}

return [MWLocalizedString(@"share-article-name-on-wikipedia", nil) stringByReplacingOccurrencesOfString:@"$1" withString:self.article.url.wmf_title]; //send just the title for other sharing services
return [NSString localizedStringWithFormat:WMFLocalizedStringWithDefaultValue(@"share-article-name-on-wikipedia", nil, NSBundle.wmf_localizationBundle, @"\"%1$@\" on @Wikipedia:", @"Formatted string expressing article being on Wikipedia with at symbol handle. Please do not translate the \"@Wikipedia\" in the message, and preserve the spaces around it, as it refers specifically to the Wikipedia Twitter account. %1$@ will be an article title, which should be wrapped in the localized double quote marks."), self.article.url.wmf_title]; //send just the title for other sharing services
}

- (NSString *)activityViewController:(UIActivityViewController *)activityViewController subjectForActivityType:(nullable NSString *)activityType {
@@ -532,7 +532,7 @@ - (UIBarButtonItem *)showTableOfContentsToolbarItem {
style:UIBarButtonItemStylePlain
target:self
action:@selector(showTableOfContents:)];
_showTableOfContentsToolbarItem.accessibilityLabel = MWLocalizedString(@"table-of-contents-button-label", nil);
_showTableOfContentsToolbarItem.accessibilityLabel = WMFLocalizedStringWithDefaultValue(@"table-of-contents-button-label", nil, NSBundle.wmf_localizationBundle, @"Table of contents", @"Accessibility label for the Table of Contents button\n{{Identical|Table of contents}}");
return _showTableOfContentsToolbarItem;
}
return _showTableOfContentsToolbarItem;
@@ -546,7 +546,7 @@ - (UIBarButtonItem *)hideTableOfContentsToolbarItem {
[closeButton addTarget:self action:@selector(hideTableOfContents:) forControlEvents:UIControlEventTouchUpInside];
closeButton.frame = (CGRect){.origin = CGPointZero, .size = closeImage.size};
_hideTableOfContentsToolbarItem = [[UIBarButtonItem alloc] initWithCustomView:closeButton];
_hideTableOfContentsToolbarItem.accessibilityLabel = MWLocalizedString(@"table-of-contents-button-label", nil);
_hideTableOfContentsToolbarItem.accessibilityLabel = WMFLocalizedStringWithDefaultValue(@"table-of-contents-button-label", nil, NSBundle.wmf_localizationBundle, @"Table of contents", @"Accessibility label for the Table of Contents button\n{{Identical|Table of contents}}");
return _hideTableOfContentsToolbarItem;
}
return _hideTableOfContentsToolbarItem;
@@ -581,7 +581,7 @@ - (UIBarButtonItem *)findInPageToolbarItem {
style:UIBarButtonItemStylePlain
target:self
action:@selector(findInPageButtonPressed)];
_findInPageToolbarItem.accessibilityLabel = MWLocalizedString(@"find-in-page-button-label", nil);
_findInPageToolbarItem.accessibilityLabel = WMFLocalizedStringWithDefaultValue(@"find-in-page-button-label", nil, NSBundle.wmf_localizationBundle, @"Find in page", @"Accessibility label for the Find in Page button");
}
return _findInPageToolbarItem;
}
@@ -1471,7 +1471,7 @@ - (void)showLocation {
- (void)showDisambiguationItems {
WMFDisambiguationPagesViewController *articleListVC = [[WMFDisambiguationPagesViewController alloc] initWithArticle:self.article dataStore:self.dataStore];
articleListVC.delegate = self;
articleListVC.title = MWLocalizedString(@"page-similar-titles", nil);
articleListVC.title = WMFLocalizedStringWithDefaultValue(@"page-similar-titles", nil, NSBundle.wmf_localizationBundle, @"Similar pages", @"Label for button that shows a list of similar titles (disambiguation) for the current page");
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:articleListVC];
[self presentViewController:navController animated:YES completion:nil];
}
@@ -1533,8 +1533,8 @@ - (void)showEditorForSection:(MWKSection *)section {
}

- (void)showProtectedDialog {
UIAlertController *alert = [UIAlertController alertControllerWithTitle:MWLocalizedString(@"page-protected-can-not-edit-title", nil) message:MWLocalizedString(@"page-protected-can-not-edit", nil) preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:MWLocalizedString(@"button-ok", nil) style:UIAlertActionStyleCancel handler:NULL]];
UIAlertController *alert = [UIAlertController alertControllerWithTitle:WMFLocalizedStringWithDefaultValue(@"page-protected-can-not-edit-title", nil, NSBundle.wmf_localizationBundle, @"This page is protected", @"Title of alert dialog shown when trying to edit a page that is protected beyond what the user can edit.") message:WMFLocalizedStringWithDefaultValue(@"page-protected-can-not-edit", nil, NSBundle.wmf_localizationBundle, @"You do not have the rights to edit this page", @"Text of alert dialog shown when trying to edit a page that is protected beyond what the user can edit.") preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:WMFLocalizedStringWithDefaultValue(@"button-ok", nil, NSBundle.wmf_localizationBundle, @"OK", @"Button text for ok button used in various places\n{{Identical|OK}}") style:UIAlertActionStyleCancel handler:NULL]];
[self presentViewController:alert animated:YES completion:NULL];
}

@@ -1677,7 +1677,7 @@ - (void)commitViewController:(UIViewController *)viewControllerToCommit {

- (NSArray<id<UIPreviewActionItem>> *)previewActions {
UIPreviewAction *readAction =
[UIPreviewAction actionWithTitle:MWLocalizedString(@"button-read-now", nil)
[UIPreviewAction actionWithTitle:WMFLocalizedStringWithDefaultValue(@"button-read-now", nil, NSBundle.wmf_localizationBundle, @"Read now", @"Read now button text used in various places.")
style:UIPreviewActionStyleDefault
handler:^(UIPreviewAction *_Nonnull action,
UIViewController *_Nonnull previewViewController) {
@@ -1686,7 +1686,7 @@ - (void)commitViewController:(UIViewController *)viewControllerToCommit {
}];

UIPreviewAction *saveAction =
[UIPreviewAction actionWithTitle:[self.savedPages isSaved:self.articleURL] ? MWLocalizedString(@"button-saved-remove", nil) : MWLocalizedString(@"button-save-for-later", nil)
[UIPreviewAction actionWithTitle:[self.savedPages isSaved:self.articleURL] ? WMFLocalizedStringWithDefaultValue(@"button-saved-remove", nil, NSBundle.wmf_localizationBundle, @"Remove from saved", @"Remove from saved button text used in various places.") : WMFLocalizedStringWithDefaultValue(@"button-save-for-later", nil, NSBundle.wmf_localizationBundle, @"Save for later", @"Longer button text for save button used in various places.")
style:UIPreviewActionStyleDefault
handler:^(UIPreviewAction *_Nonnull action,
UIViewController *_Nonnull previewViewController) {
@@ -1699,7 +1699,7 @@ - (void)commitViewController:(UIViewController *)viewControllerToCommit {
}];

UIPreviewAction *shareAction =
[UIPreviewAction actionWithTitle:MWLocalizedString(@"share-custom-menu-item", nil)
[UIPreviewAction actionWithTitle:WMFLocalizedStringWithDefaultValue(@"share-custom-menu-item", nil, NSBundle.wmf_localizationBundle, @"Share...", @"Button label for text selection Share\n{{Identical|Share}}")
style:UIPreviewActionStyleDefault
handler:^(UIPreviewAction *_Nonnull action,
UIViewController *_Nonnull previewViewController) {
@@ -1715,7 +1715,7 @@ - (void)commitViewController:(UIViewController *)viewControllerToCommit {
UIPreviewAction *placeAction = nil;
if (wmfarticle.location) {
placeAction =
[UIPreviewAction actionWithTitle:MWLocalizedString(@"page-location", nil)
[UIPreviewAction actionWithTitle:WMFLocalizedStringWithDefaultValue(@"page-location", nil, NSBundle.wmf_localizationBundle, @"View on a map", @"Label for button used to show an article on the map")
style:UIPreviewActionStyleDefault
handler:^(UIPreviewAction *_Nonnull action, UIViewController *_Nonnull previewViewController) {
UIActivityViewController *shareActivityController = [self.article sharingActivityViewControllerWithTextSnippet:nil fromButton:self.shareToolbarItem shareFunnel:self.shareFunnel];
@@ -1842,16 +1842,16 @@ - (void)showTableOfContentsAndFindInPageIconPopoversIfNecessary {

- (void)showTableOfContentsButtonPopover {
[self wmf_presentDynamicHeightPopoverViewControllerForBarButtonItem:[self tableOfContentsToolbarItem]
withTitle:MWLocalizedString(@"table-of-contents-button-label", nil)
message:MWLocalizedString(@"table-of-contents-popover-description", nil)
withTitle:WMFLocalizedStringWithDefaultValue(@"table-of-contents-button-label", nil, NSBundle.wmf_localizationBundle, @"Table of contents", @"Accessibility label for the Table of Contents button\n{{Identical|Table of contents}}")
message:WMFLocalizedStringWithDefaultValue(@"table-of-contents-popover-description", nil, NSBundle.wmf_localizationBundle, @"Get an overview of articles", @"Description of Table of Contents which can appear over its icon in a tip bubble. “Overview” refers to a view of the article’s structure, not a summary of the article.")
width:230.0f
duration:3.0];
}

- (void)showFindInPageButtonPopover {
[self wmf_presentDynamicHeightPopoverViewControllerForBarButtonItem:self.findInPageToolbarItem
withTitle:MWLocalizedString(@"find-in-page-button-label", nil)
message:MWLocalizedString(@"find-in-page-popover-description", nil)
withTitle:WMFLocalizedStringWithDefaultValue(@"find-in-page-button-label", nil, NSBundle.wmf_localizationBundle, @"Find in page", @"Accessibility label for the Find in Page button")
message:WMFLocalizedStringWithDefaultValue(@"find-in-page-popover-description", nil, NSBundle.wmf_localizationBundle, @"Search text in articles", @"Description of Find in Page which can appear over its icon in a tip bubble")
width:230.0f
duration:3.0];
}
@@ -1,9 +1,9 @@

struct WMFAuthLinkLabelStrings {
/// String containing "$1" substring.
/// String containing "%1$@" substring.
var dollarSignString: String

/// String which will replace "$1" in "dollarSignString".
/// String which will replace "%1$@" in "dollarSignString".
var substitutionString: String
}

@@ -55,7 +55,7 @@ class WMFAuthLinkLabel: UILabel {
substitutionStringAttributes[NSFontAttributeName] = boldSubheadlineFont
}

assert(strings.dollarSignString.contains("$1"), "Expected dollar sign substitution placeholder not found")
assert(strings.dollarSignString.contains("%1$@"), "Expected dollar sign substitution placeholder not found")

return strings.dollarSignString.attributedString(attributes: dollarSignStringAttributes, substitutionStrings: [strings.substitutionString], substitutionAttributes: [substitutionStringAttributes])
}
@@ -72,12 +72,12 @@ class WMFChangePasswordViewController: WMFScrollViewController {
passwordField.wmf_addThinBottomBorder()
retypeField.wmf_addThinBottomBorder()

titleLabel.text = localizedStringForKeyFallingBackOnEnglish("new-password-title")
subTitleLabel.text = localizedStringForKeyFallingBackOnEnglish("new-password-instructions")
passwordField.placeholder = localizedStringForKeyFallingBackOnEnglish("field-new-password-placeholder")
retypeField.placeholder = localizedStringForKeyFallingBackOnEnglish("field-new-password-confirm-placeholder")
passwordTitleLabel.text = localizedStringForKeyFallingBackOnEnglish("field-new-password-title")
retypeTitleLabel.text = localizedStringForKeyFallingBackOnEnglish("field-new-password-confirm-title")
titleLabel.text = WMFLocalizedString("new-password-title", value:"Set your password", comment:"Title for password change interface")
subTitleLabel.text = WMFLocalizedString("new-password-instructions", value:"You logged in with a temporary password. To finish logging in set a new password here.", comment:"Instructions for password change interface")
passwordField.placeholder = WMFLocalizedString("field-new-password-placeholder", value:"enter new password", comment:"Placeholder text shown inside new password field until user taps on it")
retypeField.placeholder = WMFLocalizedString("field-new-password-confirm-placeholder", value:"re-enter new password", comment:"Placeholder text shown inside confirm new password field until user taps on it")
passwordTitleLabel.text = WMFLocalizedString("field-new-password-title", value:"New password", comment:"Title for new password field")
retypeTitleLabel.text = WMFLocalizedString("field-new-password-confirm-title", value:"Confirm new password", comment:"Title for confirm new password field")

view.wmf_configureSubviewsForDynamicType()
}
@@ -89,7 +89,7 @@ class WMFChangePasswordViewController: WMFScrollViewController {
fileprivate func save() {

guard passwordFieldsMatch() else {
WMFAlertManager.sharedInstance.showErrorAlertWithMessage(localizedStringForKeyFallingBackOnEnglish("account-creation-passwords-mismatched"), sticky: true, dismissPreviousAlerts: true, tapCallBack: nil)
WMFAlertManager.sharedInstance.showErrorAlertWithMessage(WMFLocalizedString("account-creation-passwords-mismatched", value:"Password fields do not match.", comment:"Alert shown if the user doesn't enter the same password in both password boxes"), sticky: true, dismissPreviousAlerts: true, tapCallBack: nil)
passwordField.text = nil
retypeField.text = nil
passwordField.becomeFirstResponder()
@@ -112,7 +112,7 @@ class WMFChangePasswordViewController: WMFScrollViewController {
captchaID: nil,
captchaWord: nil,
success: { _ in
let loggedInMessage = localizedStringForKeyFallingBackOnEnglish("main-menu-account-title-logged-in").replacingOccurrences(of: "$1", with: userName)
let loggedInMessage = String.localizedStringWithFormat(WMFLocalizedString("main-menu-account-title-logged-in", value:"Logged in as %1$@", comment:"Header text used when account is logged in. %1$@ will be replaced with current username."), userName)
WMFAlertManager.sharedInstance.showSuccessAlert(loggedInMessage, sticky: false, dismissPreviousAlerts: true, tapCallBack: nil)
self.dismiss(animated: true, completion: nil)
self.funnel?.logSuccess()
@@ -228,8 +228,8 @@ - (BOOL)isAccessibilityElement {

- (NSString *)accessibilityLabel {
NSInteger clockDirection = WMFRadiansToClock(self.angleRadians);
NSString *label = MWLocalizedString(@"compass-direction", nil);
label = [label stringByReplacingOccurrencesOfString:@"$1" withString:[NSString localizedStringWithFormat:@"%@", @(clockDirection)]];
NSString *label = WMFLocalizedStringWithDefaultValue(@"compass-direction", nil, NSBundle.wmf_localizationBundle, @"at %1$@ o'clock", @"Spoken description of compass direction, e.g. \"at 3 o'clock\" means \"to the right\", \"at 11 o'clock\" means \"slightly to the left\", etc. %1$@ is the hour.");
label = [NSString localizedStringWithFormat:label, [NSString localizedStringWithFormat:@"%@", @(clockDirection)]];
return label;
}

@@ -107,25 +107,25 @@ - (nullable UIColor *)headerIconBackgroundColor {
- (nullable NSString *)headerTitle {
switch (self.contentGroupKind) {
case WMFContentGroupKindContinueReading:
return MWLocalizedString(@"explore-continue-reading-heading", nil);
return WMFLocalizedStringWithDefaultValue(@"explore-continue-reading-heading", nil, NSBundle.wmf_localizationBundle, @"Continue reading", @"Text for 'Continue Reading' header");
case WMFContentGroupKindMainPage:
return MWLocalizedString(@"explore-main-page-heading", nil);
return WMFLocalizedStringWithDefaultValue(@"explore-main-page-heading", nil, NSBundle.wmf_localizationBundle, @"Today on Wikipedia", @"Text for 'Today on Wikipedia' header");
case WMFContentGroupKindRelatedPages:
return MWLocalizedString(@"explore-continue-related-heading", nil);
return WMFLocalizedStringWithDefaultValue(@"explore-continue-related-heading", nil, NSBundle.wmf_localizationBundle, @"Because you read", @"Text for 'Because you read' header");
case WMFContentGroupKindLocation:
return MWLocalizedString(@"explore-nearby-heading", nil);
return WMFLocalizedStringWithDefaultValue(@"explore-nearby-heading", nil, NSBundle.wmf_localizationBundle, @"Places near", @"Text for 'Nearby places' header. The next line of the header is the name of the nearest article.");
case WMFContentGroupKindLocationPlaceholder:
return MWLocalizedString(@"explore-nearby-placeholder-heading", nil);
return WMFLocalizedStringWithDefaultValue(@"explore-nearby-placeholder-heading", nil, NSBundle.wmf_localizationBundle, @"Places", @"Nearby placeholder heading. The user hasn't granted location access so we show a generic section about Places on Wikipedia\n{{Identical|Place}}");
case WMFContentGroupKindPictureOfTheDay:
return MWLocalizedString(@"explore-potd-heading", nil);
return WMFLocalizedStringWithDefaultValue(@"explore-potd-heading", nil, NSBundle.wmf_localizationBundle, @"Picture of the day", @"Text for 'Picture of the day' header");
case WMFContentGroupKindRandom:
return MWLocalizedString(@"explore-random-article-heading", nil);
return WMFLocalizedStringWithDefaultValue(@"explore-random-article-heading", nil, NSBundle.wmf_localizationBundle, @"Random article", @"Text for 'Random article' header\n{{Identical|Random article}}");
case WMFContentGroupKindFeaturedArticle:
return MWLocalizedString(@"explore-featured-article-heading", nil);
return WMFLocalizedStringWithDefaultValue(@"explore-featured-article-heading", nil, NSBundle.wmf_localizationBundle, @"Featured article", @"Text for 'Featured article' header");
case WMFContentGroupKindTopRead:
return [self stringWithLocalizedCurrentSiteLanguageReplacingPlaceholderInString:MWLocalizedString(@"explore-most-read-heading", nil) fallingBackOnGenericString:MWLocalizedString(@"explore-most-read-generic-heading", nil)];
return [self stringWithLocalizedCurrentSiteLanguageReplacingPlaceholderInString:WMFLocalizedStringWithDefaultValue(@"explore-most-read-heading", nil, NSBundle.wmf_localizationBundle, @"Top read on %1$@ Wikipedia", @"Text for 'Most read articles' explore section header. %1$@ is substituted for the localized language name (e.g. 'English' or 'Espanol').") fallingBackOnGenericString:WMFLocalizedStringWithDefaultValue(@"explore-most-read-generic-heading", nil, NSBundle.wmf_localizationBundle, @"Top read", @"Text for 'Most read articles' explore section header used when no language is present")];
case WMFContentGroupKindNews:
return MWLocalizedString(@"in-the-news-title", nil);
return WMFLocalizedStringWithDefaultValue(@"in-the-news-title", nil, NSBundle.wmf_localizationBundle, @"In the news", @"Title for the 'In the news' notification & feed section");
case WMFContentGroupKindNotification:
break;
case WMFContentGroupKindAnnouncement:
@@ -145,7 +145,7 @@ - (NSString *)stringWithLocalizedCurrentSiteLanguageReplacingPlaceholderInString

//crash protection if language is nil
if (language) {
result = [string stringByReplacingOccurrencesOfString:@"$1" withString:language];
result = [NSString localizedStringWithFormat:string, language];
} else {
result = genericString;
}
@@ -165,19 +165,19 @@ - (nullable NSString *)headerSubTitle {
return self.articleURL.wmf_title;
case WMFContentGroupKindLocation: {
if (self.isForToday) {
return MWLocalizedString(@"explore-nearby-sub-heading-your-location", nil);
return WMFLocalizedStringWithDefaultValue(@"explore-nearby-sub-heading-your-location", nil, NSBundle.wmf_localizationBundle, @"Your location", @"Subtext beneath the 'Places near' header when showing articles near the user's current location.");
} else if (self.placemark) {
return [NSString stringWithFormat:@"%@, %@", self.placemark.name, self.placemark.locality];
} else {
return [NSString stringWithFormat:@"%f, %f", self.location.coordinate.latitude, self.location.coordinate.longitude];
}
} break;
case WMFContentGroupKindLocationPlaceholder:
return [self stringWithLocalizedCurrentSiteLanguageReplacingPlaceholderInString:MWLocalizedString(@"explore-nearby-placeholder-sub-heading-on-language-wikipedia", nil) fallingBackOnGenericString:MWLocalizedString(@"explore-nearby-placeholder-sub-heading-on-wikipedia", nil)];
return [self stringWithLocalizedCurrentSiteLanguageReplacingPlaceholderInString:WMFLocalizedStringWithDefaultValue(@"explore-nearby-placeholder-sub-heading-on-language-wikipedia", nil, NSBundle.wmf_localizationBundle, @"On %1$@ Wikipedia", @"Subtext beneath the 'Places' header when describing which specific Wikipedia. %1$@ will be replaced with the language - for example, 'On English Wikipedia'") fallingBackOnGenericString:WMFLocalizedStringWithDefaultValue(@"explore-nearby-placeholder-sub-heading-on-wikipedia", nil, NSBundle.wmf_localizationBundle, @"On Wikipedia", @"Subtext beneath the 'Places' header when the specific language wikipedia is unknown.")];
case WMFContentGroupKindPictureOfTheDay:
return [[NSDateFormatter wmf_dayNameMonthNameDayOfMonthNumberDateFormatter] stringFromDate:self.date];
case WMFContentGroupKindRandom:
return MWSiteLocalizedString(self.siteURL, @"onboarding-wikipedia", nil);
return WMFLocalizedStringWithDefaultValue(@"onboarding-wikipedia", self.siteURL, NSBundle.wmf_localizationBundle, @"Wikipedia", @"Wikipedia logo text\n{{Identical|Wikipedia}}");
case WMFContentGroupKindFeaturedArticle:
return [[NSDateFormatter wmf_dayNameMonthNameDayOfMonthNumberDateFormatter] stringFromDate:self.date];
case WMFContentGroupKindTopRead: {
@@ -506,27 +506,25 @@ - (nullable NSString *)footerText {
case WMFContentGroupKindMainPage:
break;
case WMFContentGroupKindRelatedPages:
return
[MWLocalizedString(@"home-more-like-footer", nil) stringByReplacingOccurrencesOfString:@"$1"
withString:self.articleURL.wmf_title];
return self.moreLikeTitle;
case WMFContentGroupKindLocation: {
if (self.isForToday) {
return MWLocalizedString(@"home-nearby-footer", nil);
return WMFLocalizedStringWithDefaultValue(@"home-nearby-footer", nil, NSBundle.wmf_localizationBundle, @"More from nearby your location", @"Footer for presenting user option to see longer list of nearby articles.");
} else {
return [MWLocalizedString(@"home-nearby-location-footer", nil) stringByReplacingOccurrencesOfString:@"$1" withString:self.placemark.name];
return [NSString localizedStringWithFormat:WMFLocalizedStringWithDefaultValue(@"home-nearby-location-footer", nil, NSBundle.wmf_localizationBundle, @"More nearby %1$@", @"Footer for presenting user option to see longer list of articles nearby a specific location. %1$@ will be replaced with the name of the location"), self.placemark.name];
}
}
case WMFContentGroupKindLocationPlaceholder: {
if (self.isForToday) {
return MWLocalizedString(@"home-nearby-footer", nil);
return WMFLocalizedStringWithDefaultValue(@"home-nearby-footer", nil, NSBundle.wmf_localizationBundle, @"More from nearby your location", @"Footer for presenting user option to see longer list of nearby articles.");
} else {
return [MWLocalizedString(@"home-nearby-location-footer", nil) stringByReplacingOccurrencesOfString:@"$1" withString:self.placemark.name];
return [NSString localizedStringWithFormat:WMFLocalizedStringWithDefaultValue(@"home-nearby-location-footer", nil, NSBundle.wmf_localizationBundle, @"More nearby %1$@", @"Footer for presenting user option to see longer list of articles nearby a specific location. %1$@ will be replaced with the name of the location"), self.placemark.name];
}
}
case WMFContentGroupKindPictureOfTheDay:
break;
case WMFContentGroupKindRandom:
return MWLocalizedString(@"explore-another-random", nil);
return WMFLocalizedStringWithDefaultValue(@"explore-another-random", nil, NSBundle.wmf_localizationBundle, @"Another random article", @"Displayed on buttons that indicate they would load 'Another random article'");
case WMFContentGroupKindFeaturedArticle:
break;
case WMFContentGroupKindTopRead: {
@@ -536,8 +534,7 @@ - (nullable NSString *)footerText {
}

return
[MWLocalizedString(@"explore-most-read-footer-for-date", nil) stringByReplacingOccurrencesOfString:@"$1"
withString:dateString];
[NSString localizedStringWithFormat:WMFLocalizedStringWithDefaultValue(@"explore-most-read-footer-for-date", nil, NSBundle.wmf_localizationBundle, @"All top read articles on %1$@", @"Text which shown on the footer beneath 'Most read articles', which presents a longer list of 'most read' articles for a given date when tapped. %1$@ will be substituted with the date"), dateString];
}
case WMFContentGroupKindNews:
break;
@@ -585,16 +582,20 @@ - (WMFFeedMoreType)moreType {
return WMFFeedMoreTypeNone;
}

- (nullable NSString *)moreLikeTitle {
return [NSString localizedStringWithFormat:WMFLocalizedStringWithDefaultValue(@"home-more-like-footer", nil, NSBundle.wmf_localizationBundle, @"More like %1$@", @"Footer for presenting user option to see longer list of articles related to a previously read article. %1$@ will be replaced with the name of the previously read article."), self.articleURL.wmf_title];
}

- (nullable NSString *)moreTitle {
switch (self.contentGroupKind) {
case WMFContentGroupKindContinueReading:
break;
case WMFContentGroupKindMainPage:
break;
case WMFContentGroupKindRelatedPages:
return [MWLocalizedString(@"home-more-like-footer", nil) stringByReplacingOccurrencesOfString:@"$1" withString:self.articleURL.wmf_title];
return self.moreLikeTitle;
case WMFContentGroupKindLocation:
return MWLocalizedString(@"main-menu-nearby", nil);
return WMFLocalizedStringWithDefaultValue(@"main-menu-nearby", nil, NSBundle.wmf_localizationBundle, @"Nearby", @"Button for showing nearby articles.\n{{Identical|Nearby}}");
case WMFContentGroupKindLocationPlaceholder:
break;
case WMFContentGroupKindPictureOfTheDay:
@@ -751,9 +752,7 @@ - (NSString *)localContentDateShortDisplayString {

- (NSString *)topReadMoreTitleForDate:(NSDate *)date {
return
[MWLocalizedString(@"explore-most-read-more-list-title-for-date", nil) stringByReplacingOccurrencesOfString:@"$1"
withString:
[[NSDateFormatter wmf_utcShortDayNameShortMonthNameDayOfMonthNumberDateFormatter] stringFromDate:date]];
[NSString localizedStringWithFormat:WMFLocalizedStringWithDefaultValue(@"explore-most-read-more-list-title-for-date", nil, NSBundle.wmf_localizationBundle, @"Top on %1$@", @"Title with date for the view displaying longer list of top read articles. %1$@ will be substituted with the date"), [[NSDateFormatter wmf_utcShortDayNameShortMonthNameDayOfMonthNumberDateFormatter] stringFromDate:date]];
}

- (NSString *)localDateDisplayString {
@@ -40,17 +40,17 @@ + (instancetype)blankEmptyView {
+ (instancetype)noFeedEmptyView {
WMFEmptyView *view = [[self class] emptyView];
view.imageView.image = [UIImage imageNamed:@"no-internet"];
view.titleLabel.text = MWLocalizedString(@"empty-no-feed-title", nil);
view.messageLabel.text = MWLocalizedString(@"empty-no-feed-message", nil);
view.actionLabel.text = MWLocalizedString(@"empty-no-feed-action-message", nil);
view.titleLabel.text = WMFLocalizedStringWithDefaultValue(@"empty-no-feed-title", nil, NSBundle.wmf_localizationBundle, @"No Internet Connection", @"Title of messsage shown in place of feed when no content could be loaded. Indicates there is no internet available");
view.messageLabel.text = WMFLocalizedStringWithDefaultValue(@"empty-no-feed-message", nil, NSBundle.wmf_localizationBundle, @"You can see your recommended articles when you have internet", @"Body of messsage shown in place of content when no feed could be loaded. Tells users they can see the articles when the interent is restored");
view.actionLabel.text = WMFLocalizedStringWithDefaultValue(@"empty-no-feed-action-message", nil, NSBundle.wmf_localizationBundle, @"You can still read saved pages", @"Footer messsage shown in place of content when no feed could be loaded. Tells users they can read saved pages offline");

return view;
}

+ (instancetype)noArticleEmptyView {
WMFEmptyView *view = [[self class] emptyView];
view.imageView.image = [UIImage imageNamed:@"no-article"];
view.messageLabel.text = MWLocalizedString(@"empty-no-article-message", nil);
view.messageLabel.text = WMFLocalizedStringWithDefaultValue(@"empty-no-article-message", nil, NSBundle.wmf_localizationBundle, @"Sorry, could not load the article", @"Shown when an article cant be loaded in place of an article");

[view.titleLabel removeFromSuperview];
[view.actionLabel removeFromSuperview];
@@ -60,7 +60,7 @@ + (instancetype)noArticleEmptyView {

+ (instancetype)noSearchResultsEmptyView {
WMFEmptyView *view = [[self class] emptyView];
view.messageLabel.text = MWLocalizedString(@"empty-no-search-results-message", nil);
view.messageLabel.text = WMFLocalizedStringWithDefaultValue(@"empty-no-search-results-message", nil, NSBundle.wmf_localizationBundle, @"No results found", @"Shown when there are no search results");

[view.imageView removeFromSuperview];
[view.titleLabel removeFromSuperview];
@@ -72,8 +72,8 @@ + (instancetype)noSearchResultsEmptyView {
+ (instancetype)noSavedPagesEmptyView {
WMFEmptyView *view = [[self class] emptyView];
view.imageView.image = [UIImage imageNamed:@"saved-blank"];
view.titleLabel.text = MWLocalizedString(@"empty-no-saved-pages-title", nil);
view.messageLabel.text = MWLocalizedString(@"empty-no-saved-pages-message", nil);
view.titleLabel.text = WMFLocalizedStringWithDefaultValue(@"empty-no-saved-pages-title", nil, NSBundle.wmf_localizationBundle, @"No saved pages yet", @"Title of a blank screen shown when a user has no saved pages");
view.messageLabel.text = WMFLocalizedStringWithDefaultValue(@"empty-no-saved-pages-message", nil, NSBundle.wmf_localizationBundle, @"Save pages to view them later, even offline", @"Message of a blank screen shown when a user has no saved pages");

[view.actionLabel removeFromSuperview];
[view.actionLine removeFromSuperview];
@@ -83,8 +83,8 @@ + (instancetype)noSavedPagesEmptyView {
+ (instancetype)noHistoryEmptyView {
WMFEmptyView *view = [[self class] emptyView];
view.imageView.image = [UIImage imageNamed:@"recent-blank"];
view.titleLabel.text = MWLocalizedString(@"empty-no-history-title", nil);
view.messageLabel.text = MWLocalizedString(@"empty-no-history-message", nil);
view.titleLabel.text = WMFLocalizedStringWithDefaultValue(@"empty-no-history-title", nil, NSBundle.wmf_localizationBundle, @"No history to show", @"Title of a blank screen shown when a user has no history");
view.messageLabel.text = WMFLocalizedStringWithDefaultValue(@"empty-no-history-message", nil, NSBundle.wmf_localizationBundle, @"Keep track of what you've been reading here", @"Message of a blank screen shown when a user has no history");

[view.actionLabel removeFromSuperview];
[view.actionLine removeFromSuperview];
@@ -100,7 +100,7 @@ @implementation WMFExploreViewController

- (void)awakeFromNib {
[super awakeFromNib];
self.title = MWLocalizedString(@"home-title", nil);
self.title = WMFLocalizedStringWithDefaultValue(@"home-title", nil, NSBundle.wmf_localizationBundle, @"Explore", @"Title for home interface.\n{{Identical|Explore}}");
self.sectionChanges = [NSMutableArray arrayWithCapacity:10];
self.objectChanges = [NSMutableArray arrayWithCapacity:10];
self.sectionCounts = [NSMutableArray arrayWithCapacity:100];
@@ -981,25 +981,25 @@ - (nullable UIAlertController *)menuActionSheetForSection:(WMFContentGroup *)sec
case WMFContentGroupKindRelatedPages: {
NSURL *url = [section headerContentURL];
UIAlertController *sheet = [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:UIAlertControllerStyleActionSheet];
[sheet addAction:[UIAlertAction actionWithTitle:MWLocalizedString(@"home-hide-suggestion-prompt", nil)
[sheet addAction:[UIAlertAction actionWithTitle:WMFLocalizedStringWithDefaultValue(@"home-hide-suggestion-prompt", nil, NSBundle.wmf_localizationBundle, @"Hide this suggestion", @"Title of button shown for users to confirm the hiding of a suggestion in the explore feed")
style:UIAlertActionStyleDestructive
handler:^(UIAlertAction *_Nonnull action) {
[self.userStore setIsExcludedFromFeed:YES withArticleURL:url];
[self.userStore.viewContext removeContentGroup:section];
}]];
[sheet addAction:[UIAlertAction actionWithTitle:MWLocalizedString(@"home-hide-suggestion-cancel", nil) style:UIAlertActionStyleCancel handler:NULL]];
[sheet addAction:[UIAlertAction actionWithTitle:WMFLocalizedStringWithDefaultValue(@"home-hide-suggestion-cancel", nil, NSBundle.wmf_localizationBundle, @"Cancel", @"Title of the button for cancelling the hiding of an explore feed suggestion\n{{Identical|Cancel}}") style:UIAlertActionStyleCancel handler:NULL]];
return sheet;
}
case WMFContentGroupKindLocationPlaceholder: {
UIAlertController *sheet = [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:UIAlertControllerStyleActionSheet];
[sheet addAction:[UIAlertAction actionWithTitle:MWLocalizedString(@"explore-nearby-placeholder-dismiss", nil)
[sheet addAction:[UIAlertAction actionWithTitle:WMFLocalizedStringWithDefaultValue(@"explore-nearby-placeholder-dismiss", nil, NSBundle.wmf_localizationBundle, @"Dismiss", @"Action button that will dismiss the nearby placeholder\n{{Identical|Dismiss}}")
style:UIAlertActionStyleDestructive
handler:^(UIAlertAction *_Nonnull action) {
[[NSUserDefaults wmf_userDefaults] wmf_setExploreDidPromptForLocationAuthorization:YES];
section.wasDismissed = YES;
[section updateVisibility];
}]];
[sheet addAction:[UIAlertAction actionWithTitle:MWLocalizedString(@"explore-nearby-placeholder-cancel", nil) style:UIAlertActionStyleCancel handler:NULL]];
[sheet addAction:[UIAlertAction actionWithTitle:WMFLocalizedStringWithDefaultValue(@"explore-nearby-placeholder-cancel", nil, NSBundle.wmf_localizationBundle, @"Cancel", @"Action button that will cancel dismissal of the nearby placeholder\n{{Identical|Cancel}}") style:UIAlertActionStyleCancel handler:NULL]];
return sheet;
}
default:
@@ -1507,13 +1507,13 @@ - (void)previewingContext:(id<UIViewControllerPreviewing>)previewingContext

- (InTheNewsViewController *)inTheNewsViewControllerForStory:(WMFFeedNewsStory *)story date:(nullable NSDate *)date {
InTheNewsViewController *vc = [[InTheNewsViewController alloc] initWithStory:story dataStore:self.userStore];
NSString *format = MWLocalizedString(@"in-the-news-title-for-date", nil);
NSString *format = WMFLocalizedStringWithDefaultValue(@"in-the-news-title-for-date", nil, NSBundle.wmf_localizationBundle, @"News on %1$@", @"Title for news on a given date - %1$@ is replaced with the date");
if (format && date) {
NSString *dateString = [[NSDateFormatter wmf_shortDayNameShortMonthNameDayOfMonthNumberDateFormatter] stringFromDate:date];
NSString *title = [format stringByReplacingOccurrencesOfString:@"$1" withString:dateString];
NSString *title = [NSString localizedStringWithFormat:format, dateString];
vc.title = title;
} else {
vc.title = MWLocalizedString(@"in-the-news-title", nil);
vc.title = WMFLocalizedStringWithDefaultValue(@"in-the-news-title", nil, NSBundle.wmf_localizationBundle, @"In the news", @"Title for the 'In the news' notification & feed section");
}
return vc;
}
@@ -473,7 +473,7 @@ - (BOOL)scheduleNotificationForNewsStory:(WMFFeedNewsStory *)newsStory
info[WMFNotificationInfoArticleExtractKey] = snippet;
}

NSString *title = MWLocalizedString(@"in-the-news-title", nil);
NSString *title = WMFLocalizedStringWithDefaultValue(@"in-the-news-title", nil, NSBundle.wmf_localizationBundle, @"In the news", @"Title for the 'In the news' notification & feed section");
NSString *body = [storyHTML wmf_stringByRemovingHTML];

NSDate *notificationDate = [NSDate date];
@@ -14,7 +14,7 @@ - (instancetype)init {
- (void)awakeFromNib {
[super awakeFromNib];

NSMutableAttributedString *attributedText = [[NSMutableAttributedString alloc] initWithString:MWLocalizedString(@"feed-news-notification-text", nil)];
NSMutableAttributedString *attributedText = [[NSMutableAttributedString alloc] initWithString:WMFLocalizedStringWithDefaultValue(@"feed-news-notification-text", nil, NSBundle.wmf_localizationBundle, @"You can now receive notifications about Wikipedia articles trending in the news.", @"Text shown to users to notify them that it is now possible to get notifications for articles related to trending news")];

[attributedText addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:13] range:NSMakeRange(0, attributedText.length)];

@@ -108,14 +108,10 @@ - (void)updateLabelTextForCurrentMatchIndex:(NSInteger)index matchesCount:(NSUIn
if (self.textField.text.length == 0) {
labelText = nil;
} else if (count > 0 && index == -1) {
labelText = [NSString stringWithFormat:@"%lu", (unsigned long)count];
labelText = [NSString localizedStringWithFormat:@"%lu", (unsigned long)count];
} else {
NSString *text = MWLocalizedString(@"find-in-page-number-matches", nil);
text = [text stringByReplacingOccurrencesOfString:@"$1" withString:[NSNumberFormatter localizedStringFromNumber:@(index + 1)
numberStyle:NSNumberFormatterDecimalStyle]];
text = [text stringByReplacingOccurrencesOfString:@"$2" withString:[NSNumberFormatter localizedStringFromNumber:@(count)
numberStyle:NSNumberFormatterDecimalStyle]];
labelText = text;
NSString *format = WMFLocalizedStringWithDefaultValue(@"find-in-page-number-matches", nil, NSBundle.wmf_localizationBundle, @"%1$d / %2$d", @"Displayed to indicate how many matches were found even if no matches. Separator can be customized depending on the language. %1$d is replaced with the numerator, %2$d is replaced with the denominator.");
labelText = [NSString localizedStringWithFormat:format, index + 1, count];
}
[self.currentMatchLabel setText:labelText];
}
@@ -23,14 +23,14 @@ class WMFForgotPasswordViewController: WMFScrollViewController {

navigationItem.leftBarButtonItem = UIBarButtonItem(image: UIImage(named:"close"), style: .plain, target:self, action:#selector(closeButtonPushed(_:)))

titleLabel.text = localizedStringForKeyFallingBackOnEnglish("forgot-password-title")
subTitleLabel.text = localizedStringForKeyFallingBackOnEnglish("forgot-password-instructions")
usernameField.placeholder = localizedStringForKeyFallingBackOnEnglish("field-username-placeholder")
emailField.placeholder = localizedStringForKeyFallingBackOnEnglish("field-email-placeholder")
usernameTitleLabel.text = localizedStringForKeyFallingBackOnEnglish("field-username-title")
emailTitleLabel.text = localizedStringForKeyFallingBackOnEnglish("field-email-title")
resetPasswordButton.setTitle(localizedStringForKeyFallingBackOnEnglish("forgot-password-button-title"), for: .normal)
orLabel.text = localizedStringForKeyFallingBackOnEnglish("forgot-password-username-or-email-title")
titleLabel.text = WMFLocalizedString("forgot-password-title", value:"Reset password", comment:"Title for reset password interface\n{{Identical|Reset password}}")
subTitleLabel.text = WMFLocalizedString("forgot-password-instructions", value:"Fill in one of the fields below to receive password reset instructions via email", comment:"Instructions for resetting password")
usernameField.placeholder = WMFLocalizedString("field-username-placeholder", value:"enter username", comment:"Placeholder text shown inside username field until user taps on it")
emailField.placeholder = WMFLocalizedString("field-email-placeholder", value:"example@example.org", comment:"Placeholder text shown inside email address field until user taps on it")
usernameTitleLabel.text = WMFLocalizedString("field-username-title", value:"Username", comment:"Title for username field\n{{Identical|Username}}")
emailTitleLabel.text = WMFLocalizedString("field-email-title", value:"Email", comment:"Noun. Title for email address field.\n{{Identical|E-mail}}")
resetPasswordButton.setTitle(WMFLocalizedString("forgot-password-button-title", value:"Reset", comment:"Title for reset password button\n{{Identical|Reset}}"), for: .normal)
orLabel.text = WMFLocalizedString("forgot-password-username-or-email-title", value:"Or", comment:"Title shown between the username and email text fields. User only has to specify either username \"Or\" email address\n{{Identical|Or}}")

usernameField.wmf_addThinBottomBorder()
emailField.wmf_addThinBottomBorder()
@@ -108,7 +108,7 @@ class WMFForgotPasswordViewController: WMFScrollViewController {
email: email,
success: { result in
self.dismiss(animated: true, completion:nil)
WMFAlertManager.sharedInstance.showSuccessAlert(localizedStringForKeyFallingBackOnEnglish("forgot-password-email-sent"), sticky: true, dismissPreviousAlerts: true, tapCallBack: nil)
WMFAlertManager.sharedInstance.showSuccessAlert(WMFLocalizedString("forgot-password-email-sent", value:"An email with password reset instructions was sent", comment:"Alert text shown when password reset email is sent"), sticky: true, dismissPreviousAlerts: true, tapCallBack: nil)
}, failure:failure)
}, failure:failure)
}
@@ -54,7 +54,7 @@ - (UIBarButtonItem *)sendEmailToolbarItem {
[button sizeToFit];
[button addTarget:self action:@selector(sendEmail) forControlEvents:UIControlEventTouchUpInside];
_sendEmailToolbarItem = [[UIBarButtonItem alloc] initWithCustomView:button];
_sendEmailToolbarItem.accessibilityLabel = MWLocalizedString(@"button-report-a-bug", nil);
_sendEmailToolbarItem.accessibilityLabel = WMFLocalizedStringWithDefaultValue(@"button-report-a-bug", nil, NSBundle.wmf_localizationBundle, @"Report a bug", @"Button text for reporting a bug");
return _sendEmailToolbarItem;
}
return _sendEmailToolbarItem;
@@ -82,7 +82,7 @@ - (void)sendEmail {
vc.mailComposeDelegate = self;
[self presentViewController:vc animated:YES completion:NULL];
} else {
[[WMFAlertManager sharedInstance] showErrorAlertWithMessage:MWLocalizedString(@"no-email-account-alert", nil) sticky:NO dismissPreviousAlerts:NO tapCallBack:NULL];
[[WMFAlertManager sharedInstance] showErrorAlertWithMessage:WMFLocalizedStringWithDefaultValue(@"no-email-account-alert", nil, NSBundle.wmf_localizationBundle, @"Please setup an email account on your device and try again.", @"Displayed to the user when they try to send a feedback email, but they have never set up an account on their device") sticky:NO dismissPreviousAlerts:NO tapCallBack:NULL];
}
}

@@ -23,7 +23,7 @@ @implementation WMFHistoryTableViewController

- (void)awakeFromNib {
[super awakeFromNib];
self.title = MWLocalizedString(@"history-title", nil);
self.title = WMFLocalizedStringWithDefaultValue(@"history-title", nil, NSBundle.wmf_localizationBundle, @"History", @"Title of the history screen shown on history tab\n{{Identical|History}}");
}

- (void)dealloc {
@@ -99,9 +99,9 @@ - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInte

NSCalendar *calendar = [NSCalendar wmf_gregorianCalendar];
if ([calendar isDateInToday:date]) {
return [padding stringByAppendingString:[MWLocalizedString(@"history-section-today", nil) uppercaseString]];
return [padding stringByAppendingString:[WMFLocalizedStringWithDefaultValue(@"history-section-today", nil, NSBundle.wmf_localizationBundle, @"Today", @"Subsection label for list of articles browsed today.\n{{Identical|Today}}") uppercaseString]];
} else if ([calendar isDateInYesterday:date]) {
return [padding stringByAppendingString:[MWLocalizedString(@"history-section-yesterday", nil) uppercaseString]];
return [padding stringByAppendingString:[WMFLocalizedStringWithDefaultValue(@"history-section-yesterday", nil, NSBundle.wmf_localizationBundle, @"Yesterday", @"Subsection label for list of articles browsed yesterday.\n{{Identical|Yesterday}}") uppercaseString]];
} else {
return [padding stringByAppendingString:[[NSDateFormatter wmf_mediumDateFormatterWithoutTime] stringFromDate:date]];
}
@@ -141,19 +141,19 @@ - (BOOL)showsDeleteAllButton {
}

- (NSString *)deleteButtonText {
return MWLocalizedString(@"history-clear-all", nil);
return WMFLocalizedStringWithDefaultValue(@"history-clear-all", nil, NSBundle.wmf_localizationBundle, @"Clear", @"Text of the button shown at the top of history which deletes all history\n{{Identical|Clear}}");
}

- (NSString *)deleteAllConfirmationText {
return MWLocalizedString(@"history-clear-confirmation-heading", nil);
return WMFLocalizedStringWithDefaultValue(@"history-clear-confirmation-heading", nil, NSBundle.wmf_localizationBundle, @"Are you sure you want to delete all your recent items?", @"Heading text of delete all confirmation dialog");
}

- (NSString *)deleteText {
return MWLocalizedString(@"history-clear-delete-all", nil);
return WMFLocalizedStringWithDefaultValue(@"history-clear-delete-all", nil, NSBundle.wmf_localizationBundle, @"Yes, delete all", @"Button text for confirming delete all action");
}

- (NSString *)deleteCancelText {
return MWLocalizedString(@"history-clear-cancel", nil);
return WMFLocalizedStringWithDefaultValue(@"history-clear-cancel", nil, NSBundle.wmf_localizationBundle, @"Cancel", @"Button text for cancelling delete all action\n{{Identical|Cancel}}");
}

- (NSURL *)urlAtIndexPath:(NSIndexPath *)indexPath {
@@ -373,7 +373,7 @@ - (UIView *_Nullable)photosViewController:(NYTPhotosViewController *)photosViewC
[NSCharacterSet whitespaceAndNewlineCharacterSet]];

NSString *ownerOrFallback = imageInfo.owner ? [imageInfo.owner stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]
: MWLocalizedString(@"image-gallery-unknown-owner", nil);
: WMFLocalizedStringWithDefaultValue(@"image-gallery-unknown-owner", nil, NSBundle.wmf_localizationBundle, @"Author unknown.", @"Fallback text for when an item in the image gallery doesn't have a specified owner.");

[caption setLicense:imageInfo.license owner:ownerOrFallback];

@@ -18,7 +18,7 @@ open class WMFImageTextActivitySource: NSObject, UIActivityItemSource {
var text: String?

if activityType == UIActivityType.postToTwitter {
text = localizedStringForKeyFallingBackOnEnglish("share-on-twitter-sign-off")
text = WMFLocalizedString("share-on-twitter-sign-off", value:"via @Wikipedia", comment:"Text placed at the end of a tweet when sharing. Contains the wikipedia twitter handle")
}else if activityType == UIActivityType.postToFacebook ||
activityType == UIActivityType.mail ||
activityType == UIActivityType.postToFlickr {
@@ -65,7 +65,7 @@ - (void)prepareForReuse {
- (void)setIsPrimary:(BOOL)isPrimary {
_isPrimary = isPrimary;
if (isPrimary) {
self.primaryLabel.text = [MWLocalizedString(@"settings-primary-language", nil) uppercaseStringWithLocale:[NSLocale currentLocale]];
self.primaryLabel.text = [WMFLocalizedStringWithDefaultValue(@"settings-primary-language", nil, NSBundle.wmf_localizationBundle, @"Primary", @"Label shown next to primary language\n{{Identical|Primary}}") uppercaseStringWithLocale:[NSLocale currentLocale]];
self.primaryLabelContainerView.backgroundColor = [UIColor wmf_primaryLanguageLabelBackground];
} else {
self.primaryLabel.text = nil;
@@ -45,7 +45,7 @@ + (instancetype)languagesViewController {
WMFLanguagesViewController *languagesVC = [WMFLanguagesViewController wmf_initialViewControllerFromClassStoryboard];
NSParameterAssert(languagesVC);

languagesVC.title = MWLocalizedString(@"article-languages-label", nil);
languagesVC.title = WMFLocalizedStringWithDefaultValue(@"article-languages-label", nil, NSBundle.wmf_localizationBundle, @"Choose language", @"Header label for per-article language selector screen.\n{{Identical|Choose language}}");
languagesVC.editing = NO;
return languagesVC;
}
@@ -54,7 +54,7 @@ + (instancetype)nonPreferredLanguagesViewController {
WMFLanguagesViewController *languagesVC = [WMFLanguagesViewController wmf_initialViewControllerFromClassStoryboard];
NSParameterAssert(languagesVC);

languagesVC.title = MWLocalizedString(@"settings-my-languages", nil);
languagesVC.title = WMFLocalizedStringWithDefaultValue(@"settings-my-languages", nil, NSBundle.wmf_localizationBundle, @"My languages", @"Title for list of user's preferred languages");
languagesVC.editing = NO;
languagesVC.showPreferredLanguages = NO;

@@ -103,7 +103,7 @@ - (void)viewDidLoad {
[self.languageFilterField setReturnKeyType:UIReturnKeyDone];
}
self.languageFilterField.barTintColor = [UIColor wmf_settingsBackground];
self.languageFilterField.placeholder = MWLocalizedString(@"article-languages-filter-placeholder", nil);
self.languageFilterField.placeholder = WMFLocalizedStringWithDefaultValue(@"article-languages-filter-placeholder", nil, NSBundle.wmf_localizationBundle, @"Find language", @"Filter languages text box placeholder text.");

self.tableView.separatorStyle = UITableViewCellSeparatorStyleSingleLine;
self.filterDividerHeightConstraint.constant = 0.5f;
@@ -272,7 +272,7 @@ - (BOOL)shouldShowHeaderForSection:(NSInteger)section {
}

- (NSString *)titleForHeaderInSection:(NSInteger)section {
NSString *title = ([self isPreferredSection:section]) ? MWLocalizedString(@"article-languages-yours", nil) : MWLocalizedString(@"article-languages-others", nil);
NSString *title = ([self isPreferredSection:section]) ? WMFLocalizedStringWithDefaultValue(@"article-languages-yours", nil, NSBundle.wmf_localizationBundle, @"Your languages", @"Title for list of user's preferred languages") : WMFLocalizedStringWithDefaultValue(@"article-languages-others", nil, NSBundle.wmf_localizationBundle, @"Other languages", @"Title for list of languages not in user's preferred languages");
return [title uppercaseStringWithLocale:[NSLocale currentLocale]];
}

@@ -373,7 +373,7 @@ + (instancetype)preferredLanguagesViewController {
WMFPreferredLanguagesViewController *languagesVC = [WMFPreferredLanguagesViewController wmf_initialViewControllerFromClassStoryboard];
NSParameterAssert(languagesVC);

languagesVC.title = MWLocalizedString(@"settings-my-languages", nil);
languagesVC.title = WMFLocalizedStringWithDefaultValue(@"settings-my-languages", nil, NSBundle.wmf_localizationBundle, @"My languages", @"Title for list of user's preferred languages");

languagesVC.hideLanguageFilter = YES;
languagesVC.showNonPreferredLanguages = NO;
@@ -436,7 +436,7 @@ - (BOOL)shouldShowFooterForSection:(NSInteger)section {
- (nullable UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section {
if ([self shouldShowFooterForSection:section]) {
WMFArticleLanguagesSectionFooter *footer = (id)[tableView dequeueReusableHeaderFooterViewWithIdentifier:[WMFArticleLanguagesSectionFooter wmf_nibName]];
footer.title = MWLocalizedString(@"settings-primary-language-details", nil);
footer.title = WMFLocalizedStringWithDefaultValue(@"settings-primary-language-details", nil, NSBundle.wmf_localizationBundle, @"The first language in this list is used as the primary language for the app. Changing this language will change daily content (such as Featured Article) shown on Explore.", @"Explanation of how the first preferred language is used. \"Explore\" is {{msg-wm|Wikipedia-ios-home-title}}.");
return footer;
} else {
return nil;
@@ -491,7 +491,7 @@ + (instancetype)articleLanguagesViewControllerWithArticleURL:(NSURL *)url {

languagesVC.articleURL = url;
languagesVC.editing = NO;
languagesVC.title = MWLocalizedString(@"languages-title", nil);
languagesVC.title = WMFLocalizedStringWithDefaultValue(@"languages-title", nil, NSBundle.wmf_localizationBundle, @"Change language", @"Title for language picker\n{{Identical|Language}}");

return languagesVC;
}