diff --git a/Components/Sources/Components/Components/Editors/Source Editor/Formatter Extensions/WKSourceEditorFormatter+ButtonActions.swift b/Components/Sources/Components/Components/Editors/Source Editor/Formatter Extensions/WKSourceEditorFormatter+ButtonActions.swift
index 8dd3b76589f..63c93b0c8ef 100644
--- a/Components/Sources/Components/Components/Editors/Source Editor/Formatter Extensions/WKSourceEditorFormatter+ButtonActions.swift
+++ b/Components/Sources/Components/Components/Editors/Source Editor/Formatter Extensions/WKSourceEditorFormatter+ButtonActions.swift
@@ -16,19 +16,63 @@ extension WKSourceEditorFormatter {
func toggleFormatting(startingFormattingString: String, endingFormattingString: String, action: WKSourceEditorFormatterButtonAction, in textView: UITextView) {
- switch action {
- case .remove:
- expandSelectedRangeUpToNearestFormattingStrings(startingFormattingString: startingFormattingString, endingFormattingString: endingFormattingString, in: textView)
-
- if selectedRangeIsSurroundedByFormattingStrings(startingFormattingString: startingFormattingString, endingFormattingString: endingFormattingString, in: textView) {
- removeSurroundingFormattingStringsFromSelectedRange(startingFormattingString: startingFormattingString, endingFormattingString: endingFormattingString, in: textView)
+ if textView.selectedRange.length == 0 {
+ switch action {
+ case .remove:
+ expandSelectedRangeUpToNearestFormattingStrings(startingFormattingString: startingFormattingString, endingFormattingString: endingFormattingString, in: textView)
+
+ if selectedRangeIsSurroundedByFormattingStrings(startingFormattingString: startingFormattingString, endingFormattingString: endingFormattingString, in: textView) {
+ removeSurroundingFormattingStringsFromSelectedRange(startingFormattingString: startingFormattingString, endingFormattingString: endingFormattingString, in: textView)
+ }
+ case .add:
+ if selectedRangeIsSurroundedByFormattingStrings(startingFormattingString: startingFormattingString, endingFormattingString: endingFormattingString, in: textView) {
+ removeSurroundingFormattingStringsFromSelectedRange(startingFormattingString: startingFormattingString, endingFormattingString: endingFormattingString, in: textView)
+ } else {
+ addStringFormattingCharacters(startingFormattingString: startingFormattingString, endingFormattingString: endingFormattingString, in: textView)
+ }
}
- case .add:
- if textView.selectedRange.length == 0 &&
- selectedRangeIsSurroundedByFormattingStrings(startingFormattingString: startingFormattingString, endingFormattingString: endingFormattingString, in: textView) {
- removeSurroundingFormattingStringsFromSelectedRange(startingFormattingString: startingFormattingString, endingFormattingString: endingFormattingString, in: textView)
- } else {
- addStringFormattingCharacters(startingFormattingString: startingFormattingString, endingFormattingString: endingFormattingString, in: textView)
+ } else {
+
+ switch action {
+ case .remove:
+
+ if selectedRangeIsSurroundedByFormattingStrings(startingFormattingString: startingFormattingString, endingFormattingString: endingFormattingString, in: textView) {
+ removeSurroundingFormattingStringsFromSelectedRange(startingFormattingString: startingFormattingString, endingFormattingString: endingFormattingString, in: textView)
+ } else {
+
+ // Note the flipped formatting string params.
+ // For example, this takes selected 'text' from:
+ // Testing Strikethrough text here
+ // To:
+ // Testing Strikethrough text here
+ // We have to add formatters in reverse order to remove formatting from 'text'
+
+ addStringFormattingCharacters(startingFormattingString: endingFormattingString, endingFormattingString: startingFormattingString, in: textView)
+ }
+
+ case .add:
+
+ // Note: gross workaround to prevent italics misfire from continuing below
+ if startingFormattingString == "''" && endingFormattingString == "''" {
+ if selectedRangeIsSurroundedByFormattingString(formattingString: "''", in: textView) &&
+ selectedRangeIsSurroundedByFormattingString(formattingString: "'''", in: textView) {
+ addStringFormattingCharacters(startingFormattingString: startingFormattingString, endingFormattingString: endingFormattingString, in: textView)
+ return
+ }
+ }
+
+ // Note the flipped formatting string params.
+ // For example, this takes selected 'text' from:
+ // Testing Strikethrough text here
+ // To:
+ // Testing Strikethrough text here
+ // We have to check and remove formatters in reverse order to add formatting to 'text'
+
+ if selectedRangeIsSurroundedByFormattingStrings(startingFormattingString: endingFormattingString, endingFormattingString: startingFormattingString, in: textView) {
+ removeSurroundingFormattingStringsFromSelectedRange(startingFormattingString: endingFormattingString, endingFormattingString: startingFormattingString, in: textView)
+ } else {
+ addStringFormattingCharacters(startingFormattingString: startingFormattingString, endingFormattingString: endingFormattingString, in: textView)
+ }
}
}
}
diff --git a/Components/Sources/ComponentsObjC/WKSourceEditorFormatterBoldItalics.m b/Components/Sources/ComponentsObjC/WKSourceEditorFormatterBoldItalics.m
index f376e5eec50..262e928431d 100644
--- a/Components/Sources/ComponentsObjC/WKSourceEditorFormatterBoldItalics.m
+++ b/Components/Sources/ComponentsObjC/WKSourceEditorFormatterBoldItalics.m
@@ -278,40 +278,17 @@ - (void)updateFonts:(WKSourceEditorFonts *)fonts inAttributedString:(NSMutableAt
#pragma mark - Public
- (BOOL)attributedString:(NSMutableAttributedString *)attributedString isBoldInRange:(NSRange)range {
- __block BOOL isBold = NO;
- if (range.length == 0) {
-
- if (attributedString.length > range.location) {
- NSDictionary *attrs = [attributedString attributesAtIndex:range.location effectiveRange:nil];
-
- if (attrs[WKSourceEditorCustomKeyFontBoldItalics] != nil || attrs[WKSourceEditorCustomKeyFontBold] != nil) {
- isBold = YES;
- } else {
- // Edge case, check previous character if we are up against a closing bold or italic
- if (attrs[WKSourceEditorCustomKeyColorOrange]) {
- attrs = [attributedString attributesAtIndex:range.location - 1 effectiveRange:nil];
- if (attrs[WKSourceEditorCustomKeyFontBoldItalics] != nil || attrs[WKSourceEditorCustomKeyFontBold] != nil) {
- isBold = YES;
- }
- }
- }
- }
-
- } else {
- [attributedString enumerateAttributesInRange:range options:nil usingBlock:^(NSDictionary * _Nonnull attrs, NSRange loopRange, BOOL * _Nonnull stop) {
- if ((attrs[WKSourceEditorCustomKeyFontBoldItalics] != nil || attrs[WKSourceEditorCustomKeyFontBold] != nil) &&
- (loopRange.location == range.location && loopRange.length == range.length)) {
- isBold = YES;
- stop = YES;
- }
- }];
- }
-
-
- return isBold;
+ return [self attributedString:attributedString isFormattedInRange:range formattingKey:WKSourceEditorCustomKeyFontBold];
}
+
- (BOOL)attributedString:(NSMutableAttributedString *)attributedString isItalicsInRange:(NSRange)range {
- __block BOOL isItalics = NO;
+ return [self attributedString:attributedString isFormattedInRange:range formattingKey:WKSourceEditorCustomKeyFontItalics];
+}
+
+#pragma mark - Private
+
+- (BOOL)attributedString:(NSMutableAttributedString *)attributedString isFormattedInRange:(NSRange)range formattingKey: (NSString *)formattingKey {
+ __block BOOL isFormatted = NO;
if (range.length == 0) {
@@ -319,29 +296,38 @@ - (BOOL)attributedString:(NSMutableAttributedString *)attributedString isItalics
NSDictionary *attrs = [attributedString attributesAtIndex:range.location effectiveRange:nil];
if (attrs[WKSourceEditorCustomKeyFontBoldItalics] != nil || attrs[WKSourceEditorCustomKeyFontItalics] != nil) {
- isItalics = YES;
+ isFormatted = YES;
} else {
// Edge case, check previous character if we are up against a closing bold or italic
if (attrs[WKSourceEditorCustomKeyColorOrange]) {
attrs = [attributedString attributesAtIndex:range.location - 1 effectiveRange:nil];
- if (attrs[WKSourceEditorCustomKeyFontBoldItalics] != nil || attrs[WKSourceEditorCustomKeyFontItalics] != nil) {
- isItalics = YES;
+ if (attrs[WKSourceEditorCustomKeyFontBold] != nil || attrs[WKSourceEditorCustomKeyFontItalics] != nil) {
+ isFormatted = YES;
}
}
}
}
} else {
+
+ __block NSRange unionRange = NSMakeRange(NSNotFound, 0);
[attributedString enumerateAttributesInRange:range options:nil usingBlock:^(NSDictionary * _Nonnull attrs, NSRange loopRange, BOOL * _Nonnull stop) {
- if ((attrs[WKSourceEditorCustomKeyFontBoldItalics] != nil || attrs[WKSourceEditorCustomKeyFontItalics] != nil) &&
- (loopRange.location == range.location && loopRange.length == range.length)) {
- isItalics = YES;
+ if (attrs[WKSourceEditorCustomKeyFontBoldItalics] != nil || attrs[formattingKey] != nil) {
+ if (unionRange.location == NSNotFound) {
+ unionRange = loopRange;
+ } else {
+ unionRange = NSUnionRange(unionRange, loopRange);
+ }
stop = YES;
}
}];
+
+ if (NSEqualRanges(unionRange, range)) {
+ isFormatted = YES;
+ }
}
- return isItalics;
+ return isFormatted;
}
@end
diff --git a/Components/Sources/ComponentsObjC/WKSourceEditorFormatterTemplate.m b/Components/Sources/ComponentsObjC/WKSourceEditorFormatterTemplate.m
index 5d6272f4a8c..b2e5bc1e321 100644
--- a/Components/Sources/ComponentsObjC/WKSourceEditorFormatterTemplate.m
+++ b/Components/Sources/ComponentsObjC/WKSourceEditorFormatterTemplate.m
@@ -33,8 +33,8 @@ - (instancetype)initWithColors:(WKSourceEditorColors *)colors fonts:(WKSourceEdi
WKSourceEditorCustomKeyVerticalTemplate: [NSNumber numberWithBool:YES]
};
- _horizontalTemplateRegex = [[NSRegularExpression alloc] initWithPattern:@"\\{{2}[^\\{\\}\\n]*\\}{2}" options:0 error:nil];
- _verticalStartTemplateRegex = [[NSRegularExpression alloc] initWithPattern:@"^\\{{2}[^\\{\\}\\n]*$" options:NSRegularExpressionAnchorsMatchLines error:nil];
+ _horizontalTemplateRegex = [[NSRegularExpression alloc] initWithPattern:@"\\{{2}[^\\{\\}\\n]*(?:\\{{2}[^\\{\\}\\n]*\\}{2})*[^\\{\\}\\n]*\\}{2}" options:0 error:nil];
+ _verticalStartTemplateRegex = [[NSRegularExpression alloc] initWithPattern:@"^(?:.*)(\\{{2}[^\\{\\}\\n]*)$" options:NSRegularExpressionAnchorsMatchLines error:nil];
_verticalParameterTemplateRegex = [[NSRegularExpression alloc] initWithPattern:@"^\\s*\\|.*$" options:NSRegularExpressionAnchorsMatchLines error:nil];
_verticalEndTemplateRegex = [[NSRegularExpression alloc] initWithPattern:@"^([^\\{\\}\n]*\\}{2})(?:.)*$" options:NSRegularExpressionAnchorsMatchLines error:nil];
}
@@ -64,11 +64,12 @@ - (void)addSyntaxHighlightingToAttributedString:(nonnull NSMutableAttributedStri
options:0
range:range
usingBlock:^(NSTextCheckingResult *_Nullable result, NSMatchingFlags flags, BOOL *_Nonnull stop) {
- NSRange matchRange = [result rangeAtIndex:0];
+ NSRange fullMatch = [result rangeAtIndex:0];
+ NSRange openingTemplateRange = [result rangeAtIndex:1];
- if (matchRange.location != NSNotFound) {
- [attributedString addAttributes:self.verticalTemplateAttributes range:matchRange];
- }
+ if (fullMatch.location != NSNotFound && openingTemplateRange.location != NSNotFound) {
+ [attributedString addAttributes:self.verticalTemplateAttributes range:openingTemplateRange];
+ }
}];
[self.verticalParameterTemplateRegex enumerateMatchesInString:attributedString.string
@@ -86,12 +87,14 @@ - (void)addSyntaxHighlightingToAttributedString:(nonnull NSMutableAttributedStri
options:0
range:range
usingBlock:^(NSTextCheckingResult *_Nullable result, NSMatchingFlags flags, BOOL *_Nonnull stop) {
+
NSRange fullMatch = [result rangeAtIndex:0];
NSRange closingTemplateRange = [result rangeAtIndex:1];
- if (fullMatch.location != NSNotFound && closingTemplateRange.location != NSNotFound) {
- [attributedString addAttributes:self.verticalTemplateAttributes range:closingTemplateRange];
- }
+ if (fullMatch.location != NSNotFound && closingTemplateRange.location != NSNotFound) {
+ [attributedString addAttributes:self.verticalTemplateAttributes range:closingTemplateRange];
+ }
+
}];
}
@@ -149,13 +152,21 @@ - (BOOL)attributedString:(NSMutableAttributedString *)attributedString isHorizon
}
} else {
+ __block NSRange unionRange = NSMakeRange(NSNotFound, 0);
[attributedString enumerateAttributesInRange:range options:nil usingBlock:^(NSDictionary * _Nonnull attrs, NSRange loopRange, BOOL * _Nonnull stop) {
- if ((attrs[WKSourceEditorCustomKeyHorizontalTemplate] != nil) &&
- (loopRange.location == range.location && loopRange.length == range.length)) {
- isTemplate = YES;
- stop = YES;
+ if (attrs[WKSourceEditorCustomKeyHorizontalTemplate] != nil) {
+ if (unionRange.location == NSNotFound) {
+ unionRange = loopRange;
+ } else {
+ unionRange = NSUnionRange(unionRange, loopRange);
}
+ stop = YES;
+ }
}];
+
+ if (NSEqualRanges(unionRange, range)) {
+ isTemplate = YES;
+ }
}
return isTemplate;
diff --git a/Components/Tests/ComponentsTests/WKSourceEditorFormatterButtonActionTests.swift b/Components/Tests/ComponentsTests/WKSourceEditorFormatterButtonActionTests.swift
index 30cf8aae558..a62c02d02f1 100644
--- a/Components/Tests/ComponentsTests/WKSourceEditorFormatterButtonActionTests.swift
+++ b/Components/Tests/ComponentsTests/WKSourceEditorFormatterButtonActionTests.swift
@@ -42,7 +42,7 @@ final class WKSourceEditorFormatterButtonActionTests: XCTestCase {
XCTAssertEqual(mediator.textView.attributedText.string, "One Two Three Four")
}
- func testSingleBoldRemove() throws {
+ func testCursorBoldRemove() throws {
let text = "One '''Two''' Three Four"
mediator.textView.attributedText = NSAttributedString(string: text)
mediator.textView.selectedRange = NSRange(location: 8, length: 0) // Just a cursor inside Two
@@ -50,7 +50,7 @@ final class WKSourceEditorFormatterButtonActionTests: XCTestCase {
XCTAssertEqual(mediator.textView.attributedText.string, "One Two Three Four")
}
- func testSingleItalicsRemove() throws {
+ func testCursorItalicsRemove() throws {
let text = "One Two '''Three''' Four"
mediator.textView.attributedText = NSAttributedString(string: text)
mediator.textView.selectedRange = NSRange(location: 14, length: 0)
@@ -77,7 +77,7 @@ final class WKSourceEditorFormatterButtonActionTests: XCTestCase {
XCTAssertEqual(mediator.textView.attributedText.string, "One Two Three Four")
}
- func testSingleBoldInsertAndRemove() throws {
+ func testCursorBoldInsertAndRemove() throws {
let text = "One Two Three Four"
mediator.textView.attributedText = NSAttributedString(string: text)
mediator.textView.selectedRange = NSRange(location: 4, length: 0) // Just a cursor before Two
@@ -87,7 +87,7 @@ final class WKSourceEditorFormatterButtonActionTests: XCTestCase {
XCTAssertEqual(mediator.textView.attributedText.string, "One Two Three Four")
}
- func testSingleItalicsInsertAndRemove() throws {
+ func testCursorItalicsInsertAndRemove() throws {
let text = "One Two Three Four"
mediator.textView.attributedText = NSAttributedString(string: text)
mediator.textView.selectedRange = NSRange(location: 4, length: 0) // Just a cursor before Two
@@ -97,6 +97,46 @@ final class WKSourceEditorFormatterButtonActionTests: XCTestCase {
XCTAssertEqual(mediator.textView.attributedText.string, "One Two Three Four")
}
+ func testBoldInnerRemoveAndInsert() throws {
+ let text = "One '''Two Three Four''' Five"
+ mediator.textView.attributedText = NSAttributedString(string: text)
+ mediator.textView.selectedRange = NSRange(location: 11, length: 5) // Selected Three
+ mediator.boldItalicsFormatter?.toggleBoldFormatting(action: .remove, in: mediator.textView)
+ XCTAssertEqual(mediator.textView.attributedText.string, "One '''Two '''Three''' Four''' Five")
+ mediator.boldItalicsFormatter?.toggleBoldFormatting(action: .remove, in: mediator.textView)
+ XCTAssertEqual(mediator.textView.attributedText.string, "One '''Two Three Four''' Five")
+ }
+
+ func testItalicsInnerRemoveAndInsert() throws {
+ let text = "One ''Two Three Four'' Five"
+ mediator.textView.attributedText = NSAttributedString(string: text)
+ mediator.textView.selectedRange = NSRange(location: 10, length: 5) // Selected Three
+ mediator.boldItalicsFormatter?.toggleItalicsFormatting(action: .remove, in: mediator.textView)
+ XCTAssertEqual(mediator.textView.attributedText.string, "One ''Two ''Three'' Four'' Five")
+ mediator.boldItalicsFormatter?.toggleItalicsFormatting(action: .remove, in: mediator.textView)
+ XCTAssertEqual(mediator.textView.attributedText.string, "One ''Two Three Four'' Five")
+ }
+
+ func testBoldItalicsInnerRemoveBoldAndInsert() throws {
+ let text = "One '''''Two Three Four''''' Five"
+ mediator.textView.attributedText = NSAttributedString(string: text)
+ mediator.textView.selectedRange = NSRange(location: 13, length: 5) // Selected Three
+ mediator.boldItalicsFormatter?.toggleBoldFormatting(action: .remove, in: mediator.textView)
+ XCTAssertEqual(mediator.textView.attributedText.string, "One '''''Two '''Three''' Four''''' Five")
+ mediator.boldItalicsFormatter?.toggleBoldFormatting(action: .remove, in: mediator.textView)
+ XCTAssertEqual(mediator.textView.attributedText.string, "One '''''Two Three Four''''' Five")
+ }
+
+ func testBoldItalicsInnerRemoveItalicsAndInsert() throws {
+ let text = "One '''''Two Three Four''''' Five"
+ mediator.textView.attributedText = NSAttributedString(string: text)
+ mediator.textView.selectedRange = NSRange(location: 13, length: 5) // Selected Three
+ mediator.boldItalicsFormatter?.toggleItalicsFormatting(action: .remove, in: mediator.textView)
+ XCTAssertEqual(mediator.textView.attributedText.string, "One '''''Two ''Three'' Four''''' Five")
+ mediator.boldItalicsFormatter?.toggleItalicsFormatting(action: .remove, in: mediator.textView)
+ XCTAssertEqual(mediator.textView.attributedText.string, "One '''''Two Three Four''''' Five")
+ }
+
func testTemplateInsert() throws {
let text = "One Two Three Four"
mediator.textView.attributedText = NSAttributedString(string: text)
@@ -113,7 +153,7 @@ final class WKSourceEditorFormatterButtonActionTests: XCTestCase {
XCTAssertEqual(mediator.textView.attributedText.string, "One Two Three Four")
}
- func testSingleTemplateInsertAndRemove() throws {
+ func testCursorTemplateInsertAndRemove() throws {
let text = "One Two Three Four"
mediator.textView.attributedText = NSAttributedString(string: text)
mediator.textView.selectedRange = NSRange(location: 4, length: 0) // Just a cursor before Two
diff --git a/Components/Tests/ComponentsTests/WKSourceEditorFormatterTests.swift b/Components/Tests/ComponentsTests/WKSourceEditorFormatterTests.swift
index d421a5bd65f..b7a0745cf4b 100644
--- a/Components/Tests/ComponentsTests/WKSourceEditorFormatterTests.swift
+++ b/Components/Tests/ComponentsTests/WKSourceEditorFormatterTests.swift
@@ -636,7 +636,43 @@ final class WKSourceEditorFormatterTests: XCTestCase {
XCTAssertEqual(refClosingAttributes[.foregroundColor] as! UIColor, colors.baseForegroundColor, "Incorrect ref formatting")
}
- func testVerticalStartTemplate() {
+ func testHorizontalNestedTemplate() {
+ let string = "Ford Island ({{lang-haw|Poka {{okina}}Ailana}}) is an"
+ let mutAttributedString = NSMutableAttributedString(string: string)
+
+ for formatter in formatters {
+ formatter.addSyntaxHighlighting(to: mutAttributedString, in: NSRange(location: 0, length: string.count))
+ }
+
+ var base1Range = NSRange(location: 0, length: 0)
+ let base1Attributes = mutAttributedString.attributes(at: 0, effectiveRange: &base1Range)
+
+ var templateRange = NSRange(location: 0, length: 0)
+ let templateAttributes = mutAttributedString.attributes(at: 13, effectiveRange: &templateRange)
+
+ var base2Range = NSRange(location: 0, length: 0)
+ let base2Attributes = mutAttributedString.attributes(at: 46, effectiveRange: &base2Range)
+
+ // "Ford Island ("
+ XCTAssertEqual(base1Range.location, 0, "Incorrect base formatting")
+ XCTAssertEqual(base1Range.length, 13, "Incorrect base formatting")
+ XCTAssertEqual(base1Attributes[.font] as! UIFont, fonts.baseFont, "Incorrect base formatting")
+ XCTAssertEqual(base1Attributes[.foregroundColor] as! UIColor, colors.baseForegroundColor, "Incorrect base formatting")
+
+ // "{{lang-haw|Poka {{okina}}Ailana}}"
+ XCTAssertEqual(templateRange.location, 13, "Incorrect template formatting")
+ XCTAssertEqual(templateRange.length, 33, "Incorrect template formatting")
+ XCTAssertEqual(templateAttributes[.font] as! UIFont, fonts.baseFont, "Incorrect template formatting")
+ XCTAssertEqual(templateAttributes[.foregroundColor] as! UIColor, colors.purpleForegroundColor, "Incorrect template formatting")
+
+ // ") is an"
+ XCTAssertEqual(base2Range.location, 46, "Incorrect base formatting")
+ XCTAssertEqual(base2Range.length, 7, "Incorrect base formatting")
+ XCTAssertEqual(base2Attributes[.font] as! UIFont, fonts.baseFont, "Incorrect base formatting")
+ XCTAssertEqual(base2Attributes[.foregroundColor] as! UIColor, colors.baseForegroundColor, "Incorrect base formatting")
+ }
+
+ func testVerticalStartTemplate1() {
let string = "{{Infobox officeholder"
let mutAttributedString = NSMutableAttributedString(string: string)
@@ -654,6 +690,33 @@ final class WKSourceEditorFormatterTests: XCTestCase {
XCTAssertEqual(templateAttributes[.foregroundColor] as! UIColor, colors.purpleForegroundColor, "Incorrect template formatting")
}
+ func testVerticalStartTemplate2() {
+ let string = "ending of previous sentence. {{cite web"
+ let mutAttributedString = NSMutableAttributedString(string: string)
+
+ for formatter in formatters {
+ formatter.addSyntaxHighlighting(to: mutAttributedString, in: NSRange(location: 0, length: string.count))
+ }
+
+ var baseRange = NSRange(location: 0, length: 0)
+ let baseAttributes = mutAttributedString.attributes(at: 0, effectiveRange: &baseRange)
+
+ var templateRange = NSRange(location: 0, length: 0)
+ let templateAttributes = mutAttributedString.attributes(at: 29, effectiveRange: &templateRange)
+
+ // "ending of previous sentence. "
+ XCTAssertEqual(baseRange.location, 0, "Incorrect base formatting")
+ XCTAssertEqual(baseRange.length, 29, "Incorrect base formatting")
+ XCTAssertEqual(baseAttributes[.font] as! UIFont, fonts.baseFont, "Incorrect base formatting")
+ XCTAssertEqual(baseAttributes[.foregroundColor] as! UIColor, colors.baseForegroundColor, "Incorrect base formatting")
+
+ // "ending of previous sentence. "
+ XCTAssertEqual(templateRange.location, 29, "Incorrect template formatting")
+ XCTAssertEqual(templateRange.length, 10, "Incorrect template formatting")
+ XCTAssertEqual(templateAttributes[.font] as! UIFont, fonts.baseFont, "Incorrect template formatting")
+ XCTAssertEqual(templateAttributes[.foregroundColor] as! UIColor, colors.purpleForegroundColor, "Incorrect base formatting")
+ }
+
func testVerticalParameterTemplate() {
let string = "| genus = Felis"
let mutAttributedString = NSMutableAttributedString(string: string)
diff --git a/Components/Tests/ComponentsTests/WKSourceEditorTextFrameworkMediatorTests.swift b/Components/Tests/ComponentsTests/WKSourceEditorTextFrameworkMediatorTests.swift
index 9380a2e08bc..484bc98bf65 100644
--- a/Components/Tests/ComponentsTests/WKSourceEditorTextFrameworkMediatorTests.swift
+++ b/Components/Tests/ComponentsTests/WKSourceEditorTextFrameworkMediatorTests.swift
@@ -61,6 +61,25 @@ final class WKSourceEditorTextFrameworkMediatorTests: XCTestCase {
XCTAssertFalse(selectionStates9.isItalics)
}
+ func testSelectionSpanningNonFormattedState1() throws {
+ let text = "Testing '''bold with {{template}}''' selection that spans nonbold."
+ mediator.textView.attributedText = NSAttributedString(string: text)
+
+ // "bold with {{template}}"
+ let selectionStates = mediator.selectionState(selectedDocumentRange: NSRange(location: 11, length: 22))
+ XCTAssertTrue(selectionStates.isBold)
+ XCTAssertFalse(selectionStates.isHorizontalTemplate)
+ }
+
+ func testSelectionSpanningNonFormattedState2() throws {
+ let text = "Testing {{template | '''bold'''}} selection that spans nonbold."
+ mediator.textView.attributedText = NSAttributedString(string: text)
+
+ // "template | '''bold'''"
+ let selectionStates1 = mediator.selectionState(selectedDocumentRange: NSRange(location: 10, length: 21))
+ XCTAssertFalse(selectionStates1.isBold)
+ XCTAssertTrue(selectionStates1.isHorizontalTemplate)
+ }
func testClosingBoldSelectionStateCursor() throws {
let text = "One '''Two''' Three"
mediator.textView.attributedText = NSAttributedString(string: text)
@@ -138,6 +157,15 @@ final class WKSourceEditorTextFrameworkMediatorTests: XCTestCase {
XCTAssertFalse(selectionStates1.isHorizontalTemplate)
}
+ func testHorizontalTemplateButtonSelectionStateFormattedRange() throws {
+ let text = "Testing inner formatted {{cite web | url=https://en.wikipedia.org | title = The '''Free''' Encyclopedia}} template example."
+ mediator.textView.attributedText = NSAttributedString(string: text)
+
+ // "cite web | url=https://en.wikipedia.org | title = The '''Free''' Encyclopedia"
+ let selectionStates = mediator.selectionState(selectedDocumentRange: NSRange(location: 26, length: 77))
+ XCTAssertTrue(selectionStates.isHorizontalTemplate)
+ }
+
func testStrikethroughSelectionState() throws {
let text = "Testing Strikethrough Testing."
mediator.textView.attributedText = NSAttributedString(string: text)
diff --git a/Wikipedia/Localizations/da.lproj/Localizable.strings b/Wikipedia/Localizations/da.lproj/Localizable.strings
index c2741fddef6..f5cd2276272 100644
--- a/Wikipedia/Localizations/da.lproj/Localizable.strings
+++ b/Wikipedia/Localizations/da.lproj/Localizable.strings
@@ -214,9 +214,20 @@
"diff-thanks-send-button-title" = "Send 'Tak'";
"diff-thanks-sent" = "Din 'Tak' blev sendt til $1";
"dim-images" = "Mørklæg billeder";
+"edit-bold-accessibility-label" = "Tilføj fed formatering";
+"edit-bold-remove-accessibility-label" = "Fjern fed formatering";
"edit-clear-formatting-accessibility-label" = "Fjern formatering";
"edit-comment-accessibility-label" = "Tilføj kommentarsyntaks";
+"edit-comment-remove-accessibility-label" = "Fjern kommentarsyntaks";
+"edit-decrease-indent-depth-accessibility-label" = "Formindsk indrykningsdybden";
+"edit-direction-down-accessibility-label" = "Flyt markøren ned";
+"edit-direction-left-accessibility-label" = "Flyt markøren til venstre";
+"edit-direction-right-accessibility-label" = "Flyt markøren til højre";
+"edit-direction-up-accessibility-label" = "Flyt markøren op";
+"edit-increase-indent-depth-accessibility-label" = "Øg indrykningsdybden";
"edit-link-display-text-title" = "Vis tekst";
+"edit-link-remove-link-title" = "Fjern link";
+"edit-link-title" = "Rediger link";
"edit-menu-item" = "Rediger";
"edit-minor-learn-more-text" = "Lær mere om mindre ændringer";
"edit-minor-text" = "Dette er en mindre ændring";
@@ -315,6 +326,7 @@
"field-token-title" = "Bekræftelseskode";
"field-username-placeholder" = "indtast brugernavn";
"field-username-title" = "Brugernavn";
+"filter-options-all" = "Alle";
"find-infolabel-number-matches" = "$1 / $2";
"find-replace-header" = "Find og erstat";
"find-textfield-accessibility" = "Find";
@@ -397,6 +409,37 @@
"no-internet-connection" = "Ingen internetforbindelse";
"no-internet-connection-article-reload-button" = "Vend tilbage til seneste gemte version";
"notifications-center-feed-news-notification-button-text" = "Slå push-notifikationer til";
+"notifications-center-filters-read-status-item-title-all" = "Alle";
+"notifications-center-filters-read-status-item-title-read" = "Læst";
+"notifications-center-filters-read-status-item-title-unread" = "Ulæst";
+"notifications-center-filters-read-status-section-title" = "Læsestatus";
+"notifications-center-filters-title" = "Filtre";
+"notifications-center-filters-types-item-title-all" = "Alle typer";
+"notifications-center-go-to-article" = "Artikel";
+"notifications-center-go-to-article-talk-format" = "$1 diskussionsside";
+"notifications-center-go-to-diff" = "Forskel";
+"notifications-center-go-to-talk-page" = "Diskussionsside";
+"notifications-center-go-to-user-page" = "$1's brugerside";
+"notifications-center-go-to-wikidata-item" = "Wikidata-emne";
+"notifications-center-go-to-your-talk-page" = "Din diskussionsside";
+"notifications-center-header-alert-from-agent" = "Advarsel fra $1";
+"notifications-center-inbox-title" = "Projekter";
+"notifications-center-inbox-wikimedia-projects-section-footer" = "Kun projekter, du har oprettet en konto til, vises her";
+"notifications-center-inbox-wikimedia-projects-section-title" = "Wikimedia-projekter";
+"notifications-center-inbox-wikipedias-section-title" = "Wikipedias";
+"notifications-center-language-project-name-format" = "$1 $2";
+"notifications-center-mark" = "Markér";
+"notifications-center-mark-all-as-read" = "Markér alle som læste";
+"notifications-center-mark-as-read" = "Markér som læst";
+"notifications-center-mark-as-unread" = "Markér som ulæst";
+"notifications-center-more-action-accessibility-label" = "Mere";
+"notifications-center-onboarding-modal-continue-action" = "Fortsæt";
+"notifications-center-status-all" = "Alle";
+"notifications-center-status-all-notifications" = "Alle meddelelser";
+"notifications-center-subheader-welcome" = "Velkommen!";
+"notifications-center-swipe-more" = "Mere";
+"notifications-center-type-title-welcome" = "Velkommen";
+"notifications-push-fallback-body-text" = "Ny aktivitet på Wikipedia";
"number-billions" = "$1 mia.";
"number-millions" = "$1 mio.";
"number-thousands" = "$1K";
@@ -451,6 +494,17 @@
"potd-widget-title" = "Dagens billede";
"preference-summary-eventlogging-opt-in" = "Tillad Wikimedia Foundation at indsamle oplysninger om, hvordan du bruger appen, for at gøre appen bedre.";
"preference-title-eventlogging-opt-in" = "Send anvendelsesrapporter";
+"project-name-mediawiki" = "MediaWiki";
+"project-name-wikibooks" = "Wikibooks";
+"project-name-wikidata" = "Wikidata";
+"project-name-wikimedia-commons" = "Wikimedia Commons";
+"project-name-wikinews" = "Wikinews";
+"project-name-wikiquote" = "Wikiquote";
+"project-name-wikisource" = "Wikisource";
+"project-name-wikispecies" = "Wikispecies";
+"project-name-wikiversity" = "Wikiversity";
+"project-name-wikivoyage" = "Wikivoyage";
+"project-name-wiktionary" = "Wiktionary";
"reading-list-add-generic-hint-title" = "Tilføj denne artikel til en læseliste?";
"reading-list-add-hint-title" = "Tilføj »$1« til læselisten?";
"reading-list-add-saved-button-title" = "Ja, tilføj dem til mine læselister";
@@ -511,7 +565,9 @@
"reading-themes-controls-syntax-highlighting" = "Syntaksfremhævelse";
"reference-title" = "Reference $1";
"relative-date-days-ago" = "{{PLURAL:$1|0=I dag|1=I går|$1 dage siden}}";
+"relative-date-hours-abbreviated" = "$1 time";
"relative-date-hours-ago" = "{{PLURAL:$1|0=For nyligt|$1 time siden|$1 timer siden}}";
+"relative-date-hours-ago-abbreviated" = "$1 timer siden";
"relative-date-minutes-ago" = "{{PLURAL:$1|0=Lige nu|$1 minut siden|$1 minutter siden}}";
"relative-date-years-ago" = "{{PLURAL:$1|0=Dette år|1=Sidste år|$1 år siden}}";
"replace-buttons-replace-all-accessibility" = "Erstat alle instanser";
@@ -603,6 +659,10 @@
"two-factor-login-with-regular-code" = "Brug bekræftelseskode";
"unknown-generic-text" = "Ukendt";
"variants-alert-dismiss-button" = "Nej tak";
+"watchlist-user-button-thank" = "Tak";
+"watchlist-user-button-user-contributions" = "Brugerbidrag";
+"watchlist-user-button-user-page" = "Brugerside";
+"watchlist-user-button-user-talk-page" = "Brugers diskussionsside";
"welcome-exploration-explore-feed-description" = "Anbefalet læsning og daglige artikler fra vores fællesskab";
"welcome-exploration-explore-feed-title" = "Udforsk nyhedskilde";
"welcome-exploration-on-this-day-description" = "Rejs tilbage i tid for at lære noget om hvad der skete i dag historisk";
diff --git a/Wikipedia/Localizations/pt.lproj/Localizable.strings b/Wikipedia/Localizations/pt.lproj/Localizable.strings
index f24abbfcb18..d2354efd787 100644
--- a/Wikipedia/Localizations/pt.lproj/Localizable.strings
+++ b/Wikipedia/Localizations/pt.lproj/Localizable.strings
@@ -149,6 +149,7 @@
"article-languages-label" = "Escolher língua";
"article-languages-others" = "Outras línguas";
"article-languages-yours" = "As suas línguas";
+"article-nav-edit" = "Editar";
"article-read-more-title" = "Ler mais";
"article-reference-view-title" = "Referência $1";
"article-revision-history" = "Historial de revisões do artigo";
@@ -261,6 +262,32 @@
"diff-unedited-lines-format" = "{{PLURAL:$1|$1 linha não editada|$1 linhas não editadas}}";
"diff-user-button-accessibility-text" = "Toque duas vezes para abrir o menu";
"dim-images" = "Atenuar imagens";
+"donate-accessibility-amount-button-hint" = "Premir duas vezes para selecionar o montante do donativo.";
+"donate-accessibility-donate-hint-format" = "Premir duas vezes para fazer um donativo de $1 à Wikimedia Foundation.";
+"donate-accessibility-email-opt-in-hint" = "Premir duas vezes para dar autorização à Wikimedia Foundation de lhe enviar correio eletrónico.";
+"donate-accessibility-keyboard-done-hint" = "Premir duas vezes para fechar a vista do teclado de entrada do montante.";
+"donate-accessibility-monthly-recurring-hint" = "Premir duas vezes para ativar donativos mensais automáticos deste montante.";
+"donate-accessibility-textfield-hint" = "Inserir o montante personalizado do donativo.";
+"donate-accessibility-transaction-fee-hint" = "Premir duas vezes para adicionar taxa da transação ao montante do donativo.";
+"donate-already-donated" = "Obrigado, caro benfeitor! A sua generosidade ajuda a continuar o desenvolvimento da Wikipédia e dos \"sites\" associados.";
+"donate-apple-fine-print" = "A Apple não é responsável por arrecadar fundos para este fim.";
+"donate-email-opt-in-text" = "Sim, a Wikimedia Foundation pode enviar-me correio eletrónico ocasionalmente.";
+"donate-help-frequently-asked-questions" = "Perguntas frequentes";
+"donate-help-other-ways-to-give" = "Outras formas de contribuir";
+"donate-help-problems-donating" = "Problemas no donativo?";
+"donate-help-tax-deductibility-information" = "Informação sobre dedução fiscal";
+"donate-later-title" = "Iremos lembrá-lo novamente amanhã.";
+"donate-maximum-error-text" = "Não podemos aceitar donativos superiores a $1 $2 através do nosso ''site''. Entre em contacto com a nossa equipa de contribuições substanciais em benefactors@wikimedia.org, por favor.";
+"donate-minimum-error-text" = "Selecionar um montante (mínimo $1 $2).";
+"donate-monthly-recurring-text" = "Tornar este donativo em recorrente mensal.";
+"donate-payment-method-prompt-apple-pay-button-title" = "Donativo com o Apple Pay";
+"donate-payment-method-prompt-message" = "Fazer um donativo com o Apple Pay ou escolher outro método de pagamento.";
+"donate-payment-method-prompt-other-button-title" = "Outro método de pagamento";
+"donate-payment-method-prompt-title" = "Fazer donativo com o Apple Pay?";
+"donate-success-subtitle" = "A sua generosidade tem grande significado para nós.";
+"donate-success-title" = "Obrigado!";
+"donate-title" = "Selecionar um montante";
+"donate-transaction-fee-opt-in-text" = "Acrescento generosamente $1 para cobrir as taxas de transação, de forma a receberem 100%% do meu donativo.";
"edit-bold-accessibility-label" = "Acrescentar formatação para negrito";
"edit-bold-remove-accessibility-label" = "Remover formatação de negrito";
"edit-clear-formatting-accessibility-label" = "Remover formatação";
@@ -912,6 +939,8 @@
"replace-replace-all-results-count" = "{{PLURAL:$1|$1 instância substituída|$1 instâncias substituídas}}";
"replace-textfield-accessibility" = "Substituir";
"replace-textfield-placeholder" = "Substituir por...";
+"return-button-title" = "Voltar";
+"return-to-article" = "Voltar ao artigo";
"reverted-edit-title" = "Edição revertida";
"saved-all-articles-title" = "Todos os artigos";
"saved-default-reading-list-tag" = "Esta lista não pode ser eliminada";
@@ -944,6 +973,7 @@
"settings-clear-cache-are-you-sure-title" = "Limpar dados da cache?";
"settings-clear-cache-cancel" = "Cancelar";
"settings-clear-cache-ok" = "Limpar cache";
+"settings-donate" = "Fazer donativo";
"settings-help-and-feedback" = "Ajuda e comentários";
"settings-language-bar" = "Mostrar línguas na pesquisa";
"settings-languages-feed-customization" = "Pode gerir as línguas mostradas personalizando as configurações do seu feed Explorar.";
@@ -990,6 +1020,67 @@
"share-social-mention-format" = "“$1” via Wikipédia: $2";
"sort-by-recently-added-action" = "Adicionado recentemente";
"sort-by-title-action" = "Título";
+"source-editor-accessibility-label-bold" = "Acrescentar formatação para negrito";
+"source-editor-accessibility-label-bold-selected" = "Remover formatação de negrito";
+"source-editor-accessibility-label-citation" = "Acrescentar sintaxe para referência";
+"source-editor-accessibility-label-citation-selected" = "Remover sintaxe de referência";
+"source-editor-accessibility-label-clear-formatting" = "Limpar formatação";
+"source-editor-accessibility-label-close-header-select" = "Fechar menu de estilos de texto";
+"source-editor-accessibility-label-close-main-input" = "Fechar menu de formatação de texto";
+"source-editor-accessibility-label-comment" = "Acrescentar sintaxe para comentário";
+"source-editor-accessibility-label-comment-selected" = "Remover sintaxe de comentário";
+"source-editor-accessibility-label-cursor-down" = "Mover cursor para baixo";
+"source-editor-accessibility-label-cursor-left" = "Mover cursor para a esquerda";
+"source-editor-accessibility-label-cursor-right" = "Mover cursor para a direita";
+"source-editor-accessibility-label-cursor-up" = "Mover cursor para cima";
+"source-editor-accessibility-label-find" = "Localizar na página";
+"source-editor-accessibility-label-find-button-clear" = "Limpar localizar";
+"source-editor-accessibility-label-find-button-close" = "Fechar localizar";
+"source-editor-accessibility-label-find-button-next" = "Próximo resultado localizado";
+"source-editor-accessibility-label-find-button-prev" = "Resultado localizado anterior";
+"source-editor-accessibility-label-find-text-field" = "Localizar";
+"source-editor-accessibility-label-format-heading" = "Mostrar menu de estilos de texto";
+"source-editor-accessibility-label-format-text" = "Mostrar menu de formatação de texto";
+"source-editor-accessibility-label-format-text-show-more" = "Mostrar menu de formatação de texto";
+"source-editor-accessibility-label-indent-decrease" = "Reduzir profundidade da indentação";
+"source-editor-accessibility-label-indent-increase" = "Aumentar profundidade da indentação";
+"source-editor-accessibility-label-italics" = "Acrescentar formatação para itálico";
+"source-editor-accessibility-label-italics-selected" = "Remover formatação de itálico";
+"source-editor-accessibility-label-link" = "Acrescentar sintaxe para hiperligação";
+"source-editor-accessibility-label-link-selected" = "Remover sintaxe de hiperligação";
+"source-editor-accessibility-label-media" = "Inserir multimédia";
+"source-editor-accessibility-label-ordered" = "Tornar a linha corrente numa lista ordenada";
+"source-editor-accessibility-label-ordered-selected" = "Remover lista ordenada da linha corrente";
+"source-editor-accessibility-label-replace-button-clear" = "Limpar substituir";
+"source-editor-accessibility-label-replace-button-perform-format" = "Executar a operação de substituição. O tipo de substituição está definido como $1";
+"source-editor-accessibility-label-replace-button-switch-format" = "Alternar o tipo de substituição. O tipo atual é $1. Selecionar para alterar.";
+"source-editor-accessibility-label-replace-text-field" = "Substituir";
+"source-editor-accessibility-label-replace-type-all" = "Substituir todas as instâncias";
+"source-editor-accessibility-label-replace-type-single" = "Substituir a instância";
+"source-editor-accessibility-label-strikethrough" = "Acrescentar riscado";
+"source-editor-accessibility-label-strikethrough-selected" = "Remover riscado";
+"source-editor-accessibility-label-subscript" = "Acrescentar formatação para subscrito";
+"source-editor-accessibility-label-subscript-selected" = "Remover formatação de subscrito";
+"source-editor-accessibility-label-superscript" = "Acrescentar formatação para sobrescrito";
+"source-editor-accessibility-label-superscript-selected" = "Remover formatação de sobrescrito";
+"source-editor-accessibility-label-template" = "Acrescentar sintaxe para predefinição";
+"source-editor-accessibility-label-template-selected" = "Remover sintaxe de predefinição";
+"source-editor-accessibility-label-underline" = "Acrescentar sublinha";
+"source-editor-accessibility-label-underline-selected" = "Remover sublinha";
+"source-editor-accessibility-label-unordered" = "Tornar a linha corrente numa lista não ordenada";
+"source-editor-accessibility-label-unordered-selected" = "Remover lista não ordenada da linha corrente";
+"source-editor-clear-formatting" = "Limpar formatação";
+"source-editor-find-replace-all" = "Substituir todas";
+"source-editor-find-replace-single" = "Substituir";
+"source-editor-find-replace-with" = "Substituir por...";
+"source-editor-heading" = "Cabeçalho";
+"source-editor-paragraph" = "Parágrafo";
+"source-editor-style" = "Estilo";
+"source-editor-subheading1" = "Subtítulo 1";
+"source-editor-subheading2" = "Subtítulo 2";
+"source-editor-subheading3" = "Subtítulo 3";
+"source-editor-subheading4" = "Subtítulo 4";
+"source-editor-text-formatting" = "Formatação de texto";
"table-of-contents-button-label" = "Índice";
"table-of-contents-close-accessibility-hint" = "Fechar";
"table-of-contents-close-accessibility-label" = "Fechar índice";
@@ -1145,6 +1236,45 @@
"watchlist-empty-view-filter-title" = "Não tem páginas vigiadas";
"watchlist-empty-view-subtitle" = "Acompanhe o que está a acontecer a artigos do seu interesse. Toque no menu do artigo e selecione “Vigiar” para ver cada alteração de um artigo.";
"watchlist-empty-view-title" = "Os artigos que adicionou às páginas vigiadas aparecem aqui";
+"watchlist-expiration-subtitle" = "As páginas permanecem na lista por padrão, mas existem opções para vigilância temporária entre uma semana a um ano.";
+"watchlist-expiration-title" = "Definir expiração";
+"watchlist-filter" = "Filtrar";
+"watchlist-filter-activity-header" = "Atividade das páginas vigiadas";
+"watchlist-filter-activity-options-seen-changes" = "Mudanças já vistas";
+"watchlist-filter-activity-options-unseen-changes" = "Mudanças ainda não vistas";
+"watchlist-filter-automated-contributions-header" = "Contribuições automatizadas";
+"watchlist-filter-automated-contributions-options-bot" = "Robô";
+"watchlist-filter-automated-contributions-options-human" = "Ser humano (não robô)";
+"watchlist-filter-latest-revisions-header" = "Últimas revisões";
+"watchlist-filter-latest-revisions-options-latest-revision" = "Última revisão";
+"watchlist-filter-latest-revisions-options-not-latest-revision" = "Exceto a última revisão";
+"watchlist-filter-significance-header" = "Importância";
+"watchlist-filter-significance-options-minor-edits" = "Edições menores";
+"watchlist-filter-significance-options-non-minor-edits" = "Edições não-menores";
+"watchlist-filter-type-of-change-header" = "Tipo de mudança";
+"watchlist-filter-type-of-change-options-category-changes" = "Alterações de categoria";
+"watchlist-filter-type-of-change-options-logged-actions" = "Operações registadas";
+"watchlist-filter-type-of-change-options-page-creations" = "Criações de páginas";
+"watchlist-filter-type-of-change-options-page-edits" = "Edições de páginas";
+"watchlist-filter-type-of-change-options-wikidata-edits" = "Edições de Wikidata";
+"watchlist-filter-user-registration-header" = "Registo e experiência dos utilizadores";
+"watchlist-filter-user-registration-options-registered" = "Registados";
+"watchlist-filter-user-registration-options-unregistered" = "Não registados";
+"watchlist-number-filters" = "Modificar [{{PLURAL:$1|$1 filtro|$1 filtros}}](wikipedia://watchlist/filter) para ver mais elementos da lista de páginas vigiadas";
+"watchlist-onboarding-button-title" = "Saber mais sobre a lista de páginas vigiadas";
+"watchlist-onboarding-title" = "Apresentação da lista de páginas vigiadas";
+"watchlist-removed" = "Removida das suas páginas vigiadas";
+"watchlist-thanks-success" = "O seu agradecimento foi enviado a $1";
+"watchlist-track-subtitle" = "A lista de páginas vigiadas é uma ferramenta que lhe permite acompanhar as mudanças feitas nas páginas ou artigos do seu interesse.";
+"watchlist-track-title" = "Monitorização de mudanças";
+"watchlist-updates-subtitle" = "A lista das páginas vigiadas que adicionou, como edições ou discussões, pode ser acedida em Definições → Conta.";
+"watchlist-updates-title" = "Ver atualizações";
+"watchlist-user-button-thank" = "Agradecer";
+"watchlist-user-button-user-contributions" = "Contribuições do utilizador";
+"watchlist-user-button-user-page" = "Página de utilizador";
+"watchlist-user-button-user-talk-page" = "Página de discussão do utilizador";
+"watchlist-watch-subtitle" = "Premindo a estrela ou a operação \"Vigiar\" na barra de ferramentas inferior de um artigo, pode adicionar essa página à sua lista de páginas vigiadas.";
+"watchlist-watch-title" = "Vigiar artigos";
"welcome-exploration-explore-feed-description" = "Leitura recomendada e artigos diários da nossa comunidade";
"welcome-exploration-explore-feed-title" = "O feed \"Explorar\"";
"welcome-exploration-on-this-day-description" = "Retroceda no tempo para saber o que aconteceu hoje na história";
diff --git a/Wikipedia/Localizations/th.lproj/Localizable.strings b/Wikipedia/Localizations/th.lproj/Localizable.strings
index bef3726e72b..a3b6fb084a3 100644
--- a/Wikipedia/Localizations/th.lproj/Localizable.strings
+++ b/Wikipedia/Localizations/th.lproj/Localizable.strings
@@ -829,6 +829,7 @@
"vanish-account-learn-more-text" = "เรียนรู้เพิ่มเติม";
"vanish-account-warning-title" = "คำเตือน";
"variants-alert-dismiss-button" = "ไม่ขอบคุณ";
+"watchlist-user-button-user-page" = "หน้าผู้ใช้";
"welcome-exploration-explore-feed-description" = "บทความแนะนำประจำวันจากชุมชนของเรา";
"welcome-exploration-explore-feed-title" = "ฟีดสำรวจ";
"welcome-exploration-on-this-day-description" = "ย้อนเวลากลับไปเพื่อเรียนรู้ว่าเกิดอะไรขึ้นในเวลาที่ผ่านมา";
diff --git a/Wikipedia/iOS Native Localizations/da.lproj/Localizable.strings b/Wikipedia/iOS Native Localizations/da.lproj/Localizable.strings
index 3fa7a7a928a..ad6045fcf9c 100644
Binary files a/Wikipedia/iOS Native Localizations/da.lproj/Localizable.strings and b/Wikipedia/iOS Native Localizations/da.lproj/Localizable.strings differ
diff --git a/Wikipedia/iOS Native Localizations/pt.lproj/Localizable.strings b/Wikipedia/iOS Native Localizations/pt.lproj/Localizable.strings
index 633af1864eb..7d93c3a6044 100644
Binary files a/Wikipedia/iOS Native Localizations/pt.lproj/Localizable.strings and b/Wikipedia/iOS Native Localizations/pt.lproj/Localizable.strings differ
diff --git a/Wikipedia/iOS Native Localizations/pt.lproj/Localizable.stringsdict b/Wikipedia/iOS Native Localizations/pt.lproj/Localizable.stringsdict
index 73c606b64ee..af164634516 100644
--- a/Wikipedia/iOS Native Localizations/pt.lproj/Localizable.stringsdict
+++ b/Wikipedia/iOS Native Localizations/pt.lproj/Localizable.stringsdict
@@ -813,5 +813,21 @@
%1$d bytes
+ watchlist-number-filters
+
+ NSStringLocalizedFormatKey
+ Modificar [%#@v1@](wikipedia://watchlist/filter) para ver mais elementos da lista de páginas vigiadas
+ v1
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ d
+ one
+ %1$d filtro
+ other
+ %1$d filtros
+
+
diff --git a/Wikipedia/iOS Native Localizations/th.lproj/Localizable.strings b/Wikipedia/iOS Native Localizations/th.lproj/Localizable.strings
index 9df6274fe78..3d832ec181c 100644
Binary files a/Wikipedia/iOS Native Localizations/th.lproj/Localizable.strings and b/Wikipedia/iOS Native Localizations/th.lproj/Localizable.strings differ