Everybody knows this functionality. Just press [CTRL] + [F] and you will see one of the most commonly used windows in Visual Studio – find and replace. I have been using it only for simple operations like searching simple strings and replacing them with other. Fortunately, it has also very neat function which enables you to use Regular Expressions.
Few days ago I got a task to fix some texts in one of our websites because the translation was incorrect. When I check the source code I realized I need to paste in several places in the Javascript code (because these project is using ExtJs library for view layer) which is launching the translation mechanism on client side. The operation seems to be quite simple:
- Find text: ‘something to translate’
- Replace it with text: __(‘something to translate’)
- Check and update the translation file with new text
Complex find and replace scenario
Unfortunately, the situation got more and more complicated because I noticed that except for text property there is a whole set of different ones, specially named. So I had to look for the following set of properties in Javascript files: tooltip, header, text, Text, displayMsg, emptyMsg, header, msg, waitTitle, waitMsg, value, fieldLabel, title, html.
Secondly, some of the fields already had translation function in place so there was no need to translate them again. I had to find only properties without it. The third complication occurred when I noticed that in some places the apostrophe sign was replaced by quote sign. What was also important was the fact that I should not havechanged any of ExtJS files because the results of such operations could be harmful.
So my task was truly consisting of the following steps:
- Find all occurrences of specific attributes.
- Don’t search in the ExtJS library files
- Omit already translated properties
- Find apostrophe or quote text
- Replace it with text: __(‘something to translate’)
- Don’t change property name
- Check and update the translation file with new text
Visual Studio Regular Expressions – one and only solution
I knew already that without Regular Expressions I will not do this task properly. Firstly, I carried out an Unit Test that was scanning all source files and checking them againts my custom expression. I made it but I was not sure if I can trust it and run on all of the source files. Please, note that we have currently around 40 javascript files each of them has between 500 – 1000 lines of code. If I blow one of them up, the whole system will run improperly. Therefore, I had to conclude that I need to use Find and Replace option from Visual Studio 2008 which allows us to use Regular Expression syntax.
One tricky thing about this syntax is the fact, that it is not exactly up to to a global standard. I don’t know why, and other people as well but this is the reality. Fortunately, after few minutes of coursing on it I have forced myself to using it.
Tricky queries
I have to find fieldLabel: but also fieldLabel : or fieldLabel : because all of them are correct. To find first element I could just use this query: fieldLabel*\:
To find all three of them I have to add :d symbol which enables me to search for multiple space or tab separator. So the right query would look like: fieldLabel*(:b)*\:
Okay, but I also want to search for different properties, not only fieldLabel. Fortunatel, I can do it using OR operator and brackets. So the right query to find all kind of properties should look like:
(tooltip|header|text|displayMsg|emptyMsg|header|msg|waitTitle|waitMsg|value|fieldLabel|title|html) *(:b)*\:
What is important to mention is the fact that the above statement is not case sensitive. This means that I will find displayMsg or displaymsg. Now we will find all properties we are looking for with quote or apostrophe sign. This is not the end. We have to find whole statement. Now we will find only fieldLabel:
We would like to find fieldLabel:’some text to be translated’. To do so, we have to add the following: *(:b)(‘|”).*(‘|”)
This statement says, that we will look for possible existence of white space or tab with apostrophe or quote – (‘|”). The statement .* is saying that we are looking for at least one character string ended with apostrophe or quote sign – (‘|”)
So our final string should look like following:
(tooltip|header|text|displayMsg|emptyMsg|header|msg|waitTitle|waitMsg|value|fieldLabel|title|html) *(:b)*\:*(:b)(‘|”).*(‘|”)
Shorthands- the rescue!
What is our last obstacle is the fact that in the following example, our regular expression query will select two properties in one line.
header: ‘Country’, dataIndex: ‘Country’,
This is because our query is allowing all kind of characters between quotes (apostrophes) so also other special ones. The solution is simple. Instead of using complex statement that will find several quoted texts (‘|”).*(‘|”) lets use :q
This is special shorthand which allows us to search for quoted text. This way, our final query is:
(tooltip|header|text|displayMsg|emptyMsg|header|msg|waitTitle|waitMsg|value|fieldLabel|title|html) *(:b)*\:*(:b):q
And our issue is solved because instead of having two properties marked we will have only one.
header: ‘Country’, dataIndex: ‘Country’,
Serious decision – let’s replace them all!
The first step of our task is finished. Now we have to replace found text into proper one. Of course we will use the replace function of Find and Replace window in Visual Studio editor. But what we should write there to be able to replace only the quoted text, not changing the property name? For this we have to use so called tagged expressions. Lets take our query and place in there brace brackets {}
{(tooltip|header|text|displayMsg|emptyMsg|header|msg|waitTitle|waitMsg|value|fieldLabel|title|html)} *(:b)*\:*(:b){:q}
As you can notice, I have placed my properties set into one {} and my quoted text into second {}. Now, in Replace with text box (in our Quick Replace window from Visual Studio), paste the following:
\1:__(\2)
What this will do?
Let’s use our example:
header: ‘Country’, dataIndex: ‘Country’,
The first tagged expression we pasted will take header because it exists in our set of possible properties. Because we have put it into tagged expression it also got the special index 1. This means that if you want to use the found text in Replace with text box, you just have to write \1
What is important to mention, is the fact that in Replace with text box, you don’t use regular expressions. This is just a simple text, with special tag replaced by your find result. The second tagged expression will get \2 index. We have placed it into our method call __(\2). This way, as a result of pressing Replace button (in Find what field our regular expression and in Replace with our tagged expression) we will get:
header: __(‘Country’), dataIndex: ‘Country’,
I am almost there. I know how to replace it, I just need to exclude the ExtJS libraries from my solution not to change them while running my deadly script. To do so I did very simple thing – I’ve just pressed right mouse button on the folder in my Solution Explorer and selected Exclude From Project. Now I can search through entire solution.
The next step…
As I mentioned at the beginning of this post, after replacing all occurences in Javascript files with the proper method call, I have to refresh my translation file with tne new translation texts. This file is made in XLIFF format and will store multilanguage translations so the task is also not trival. Are you interested how I did it? I will write about it soon.
You have a bug in your initial regexp that is repeated throughout the exercise.
fieldLabel*: will match 'fieldLabe:' as well as 'fieldLabel:'. And 'fieldLabelllll:' for that matter
The asterisk will match 0 or more occurrences of the preceding character.
I like writing, hurriedly, not that you are not
http://www.oantcogs.com
Any example on how to replace found upper letter with the same lower?
F.e.: _TestString -> _testString