The actual content of a working file upon checkout depends on two things: the internal file identifier recorded for it and the contents of the project descriptor. The internal file identifier designates a particular "raw" file contents stored in the repository that never changes once it is first created. The checked out file consists of this contents modified by keyword replacement. If the same file identifier is used for two files in two different projects, the checked-out working files will differ in the values supplied for these keywords. This is a subtle point, but it is significant when you copy a file identifier from one project to another.
For example, suppose that file F in version 1.2 of project P contains the line
/* $ProjectVersion: 1.2$ */
and that its entry in the Files attribute of P.prj is
(F (P/0_F 1.1))
If you decide to rename F to G for the next version of P, you can do so by changing its line in the Files attribute to
(G (P/0_F 1.1))
When you check the project back in, creating version 1.3, this same line will be retained as its file identifier (assuming, that is, you made no changes to G or its former incarnation, F). However, in version 1.3 of P, G will contain
/* $ProjectVersion: 1.3$ */
That is, PRCS actually performs all keyword replacement on check-out (think of it as lazy replacement). It only makes a user-visible difference in the handling of internal identifiers. It also has a considerable effect on the performance of partial checkins (those that contain a list of specific files and directories to check in, and copy all unmentioned files from file identifiers listed in the Files attribute). Because PRCS does not have to examine the files excluded from the file list for keywords, it can process them very quickly.