Change:
VSCode Version: 1.7.2 OS Version: Windows 10 Steps to Reproduce: a workspace -and-replace a string that exists Screenshot: !
Same for "occurrences".
This is one of those surprisingly hard localization problems... is there anywhere in the UI that we do this correctly?
Why not coming with appropriate messages based on the number of occurrences/files? Feel free to submit a PR for this if you are interested to fix it. Ref -
Because it's different for different languages - example: The only language outside of the norm that we support is Russian though.
I am sorry, I did not get what you mean. Why not having separate strings for each variation and let the translation team does the rest. For eg: Replace occurrences across files with Replace occurrences in with Replace 1 occurrence in with
Just pointing out that other languages don't follow the same pluralization rules as English. But it's already wrong in those languages and we may as well make it better for English :)
Yes, messages cannot be constructed programmatically because of translation. Translation team need to translate these different messages. Hence they need to be string literals. Thanks for the PR

Discussion:
let occurrences = this.viewModel.searchResult.count(); let fileCount = this.viewModel.searchResult.fileCount(); let replaceValue = this.searchWidget.getReplaceValue() || ''; <del> let afterReplaceAllMessage = replaceValue ? nls.localize('replaceAll.message', "Replaced {0} occurrences across {1} files with {2}.", occurrences, fileCount, replaceValue) : nls.localize('removeAll.message', "Removed {0} occurrences across {1} files.", occurrences, fileCount); </del> <ins> let afterReplaceAllMessage = this.buildAfterReplaceAllMessage(occurrences, fileCount, replaceValue); </ins> let confirmation = { title: nls.localize('replaceAll.confirmation.title', "Replace All"), <del> message: replaceValue ? nls.localize('replaceAll.confirmation.message', "Replace {0} occurrences across {1} files with '{2}'?", occurrences, fileCount, replaceValue) : nls.localize('removeAll.confirmation.message', "Remove {0} occurrences across {1} files?", occurrences, fileCount), </del> <ins> message: this.buildReplaceAllConfirmationMessage(occurrences, fileCount, replaceValue), </ins> primaryButton: nls.localize('replaceAll.confirm.button', "Replace") };

} } <ins> private buildAfterReplaceAllMessage(occurrences: number, fileCount: number, replaceValue?: string) { if (occurrences === 1) { if (fileCount === 1) { if (replaceValue) { return nls.localize('replaceAll.occurrence.file.message', "Replaced {0} occurrence across {1} file with '{2}'.", occurrences, fileCount, replaceValue); } return nls.localize('removeAll.occurrence.file.message', "Replaced {0} occurrence across {1} file'.", occurrences, fileCount); } if (replaceValue) { return nls.localize('replaceAll.occurrence.files.message', "Replaced {0} occurrence across {1} files with '{2}'.", occurrences, fileCount, replaceValue); } return nls.localize('removeAll.occurrence.files.message', "Replaced {0} occurrence across {1} files.", occurrences, fileCount); } if (fileCount === 1) { if (replaceValue) { return nls.localize('replaceAll.occurrences.file.message', "Replaced {0} occurrences across {1} file with '{2}'.", occurrences, fileCount, replaceValue); } return nls.localize('removeAll.occurrences.file.message', "Replaced {0} occurrences across {1} file'.", occurrences, fileCount); } if (replaceValue) { return nls.localize('replaceAll.occurrences.files.message', "Replaced {0} occurrences across {1} files with '{2}'.", occurrences, fileCount, replaceValue); } return nls.localize('removeAll.occurrences.files.message', "Replaced {0} occurrences across {1} files.", occurrences, fileCount); } private buildReplaceAllConfirmationMessage(occurrences: number, fileCount: number, replaceValue?: string) { if (occurrences === 1) { if (fileCount === 1) { if (replaceValue) { return nls.localize('removeAll.occurrence.file.confirmation.message', "Replace {0} occurrence across {1} file with '{2}'?", occurrences, fileCount, replaceValue); } return nls.localize('replaceAll.occurrence.file.confirmation.message', "Replace {0} occurrence across {1} file'?", occurrences, fileCount); } if (replaceValue) { return nls.localize('removeAll.occurrence.files.confirmation.message', "Replace {0} occurrence across {1} files with '{2}'?", occurrences, fileCount, replaceValue); } return nls.localize('replaceAll.occurrence.files.confirmation.message', "Replace {0} occurrence across {1} files?", occurrences, fileCount); } if (fileCount === 1) { if (replaceValue) { return nls.localize('removeAll.occurrences.file.confirmation.message', "Replace {0} occurrences across {1} file with '{2}'?", occurrences, fileCount, replaceValue); } return nls.localize('replaceAll.occurrences.file.confirmation.message', "Replace {0} occurrences across {1} file'?", occurrences, fileCount); } if (replaceValue) { return nls.localize('removeAll.occurrences.files.confirmation.message', "Replace {0} occurrences across {1} files with '{2}'?", occurrences, fileCount, replaceValue); } return nls.localize('replaceAll.occurrences.files.confirmation.message', "Replace {0} occurrences across {1} files?", occurrences, fileCount); } </ins> private clearMessage(): Builder { return this.messages.empty().show() .asContainer().div({ 'class': 'message' })