There is a certain impedance mismatch between lines of code and Mathematica expressions, because Mathematica code is written more or less directly in the parse trees, and the syntax (which encourages nested expressions) was not particularly designed to make lines a really good concept here. That said, this would be a problem in any language, to various extents, because, while you know what you meant in the code and what the error is, the parser may interpret things differently (this is the difference between syntax and semantics). For example, your particular error which you mentioned in the question will be interpreted as a bracket not closed at the end of file, not where you think it really happens (because the parser thinks that the rest of the code is within an opening bracket of Protect).
In any case, the following function will (hopefully) at least tell you what parser thinks, in terms of line numbers:
ClearAll[getErrorLine]; getErrorLine[filename_String] := Module[{code, lengths}, code = Import[filename, "Text"]; lengths = StringLength /@ StringSplit[code, "\n"] + 1; With[{sl = SyntaxLength[code]}, LengthWhile[Accumulate[lengths], # < sl &] ] ]
For a test package like this (all new lines intact):
BeginPackage["SyntaxTest`"] f::usage; Begin["`Private`"] g[x_]:= x^2; f[x_]:=Sin[g[x] End[] EndPackage[]
it gives
getErrorLine["C:\\Temp\\SyntaxTest.m"]
13
which is, at the end of the package. And this is correct, since you can not assign a well-defined semantics to a syntactically broken code, so you can only ask what parser thinks.
LineNumberConvertfunction to convert between different styles... Just a wild thought... :) $\endgroup$