Find localized Windows strings
Asked Answered
D

5

8

I need to find some strings that the current version of Windows is using. For example, when I create a new folder, it is initially named "New Folder" on English Vista. I need to programmatically find what that folder would be named on any language and version of Windows that I might be running on.

Anyone have any ideas how to do that?

Thanks Morinar -- I just stumbled upon that article too. Unfortunately, the stringID doesn't appear to be constant -- it's 30396 on my Vista, which isn't the same as what they show for XP. So it would appear MS didn't keep it stable.

EDIT: Looks like this isn't possible...? This apps runs on computers in Germany, The Netherlands, France, Spain, Brazil, Mexico, Vietnam, Taiwan, China, Japan, South Korea, India, Israel, Hungary ... You get the idea. It will take a very long time to install all the different language packs and find out what 'New Folder' is in every language.

Perhaps the best option is to default to "New Folder" and make the user change that value if they want to. I just prefer to have the software figure out as much as it can and spare the user from configuring _yet_another_setting_.

Dispossess answered 23/3, 2010 at 18:0 Comment(3)
This won't be as easy as you might think. If you create a new folder and leave it named 'New Folder', then create another new folder, you start with the name 'New Folder (2)'. The grammar of that may change from language to language (I don't know).Photoflood
Why do you need to know this?Bulbous
I wouldn't expect the resource string IDs to be stable between major releases of the OS. String resources get added and removed and recompiled between major releases, and let's face it - you're poking around in the private data of an OS module. The Windows API doesn't expose those strings so there is no contract of stability expressed or implied.Inexpedient
L
10

Shamelessly cribbed from http://blogs.msdn.com/oldnewthing/archive/2004/01/30/65013.aspx. This is mostly correct but if there is a resource string of "New Folder something else" it will match that:

LPCWSTR FindStringResourceEx(HINSTANCE hinst,
    UINT uId, UINT langId)
{
    // Convert the string ID into a bundle number
    LPCWSTR pwsz = NULL;
    HRSRC hrsrc = FindResourceEx(hinst, RT_STRING,
        MAKEINTRESOURCE(uId / 16 + 1),
        langId);
    if (hrsrc) {
        HGLOBAL hglob = LoadResource(hinst, hrsrc);
        if (hglob) {
            pwsz = reinterpret_cast<LPCWSTR>
                (LockResource(hglob));
            if (pwsz) {
                // okay now walk the string table
                for (int i = 0; i < (uId & 15); i++) {
                    pwsz += 1 + (UINT)*pwsz;
                }

                pwsz+= 1;
            }
        }
    }
    return pwsz;
}

UINT FindResourceStringId(HMODULE resource_handle, LPCWSTR string, UINT langId)
{
    UINT resource_id= -1;

    for (int i= 0; i<65536; ++i)
    {
        LPCWSTR resource_string= FindStringResourceEx(resource_handle, i, langId);

        if (resource_string && wcsncmp(resource_string, string, wcslen(string))==0)
        {
            resource_id= i;
        }
    }

    return resource_id;
}

int main()
{
    HMODULE shell_handle= LoadLibraryW(L"shell32.dll");
    UINT new_folder_id= FindResourceStringId(shell_handle, L"New Folder", 0x409); // look for US English "New Folder" resource id.
}
Lyn answered 6/4, 2010 at 23:6 Comment(3)
That's great to find the ID. Imagine the program is running in China though. What do you pass in for L"New Folder" in that case? I don't actually need the ID, I need to know the Chinese equivalent of "New Folder". And the French equivalent, German, Dutch, Vietnamese, Korean, etc, etc. :(Dispossess
@DougN, you can get the ID using the english locale, then use FindResourceStringEx(resource_handle, new_folder_id, language_id) for other languages. 0x409 is the language id for english. So if you wanted Simplified Chinese, you would use FindResourceStringEx(resource_handle, new_folder_id, 0x0804), for French FindResourceStringEx(resource_handle, new_folder_id, 0x080c), etc.Lyn
@MSN, an open question is whether in a pure chinese Windows (English language removed after selecting chinese) the english string resources are still available.Bergmann
W
5

This is not easy. These strings are private data for Windows Explorer, and as such they can (and probably do) change between releases. You can hack something up where you do a lot of version checking and read the appropriate resource string, but that seems like a losing battle. What are you trying to accomplish?

Whelm answered 23/3, 2010 at 20:57 Comment(4)
I have a file monitoring app that alerts users when new directories are created. The users don't want to be alerted about "New Folder", and then a few moments later be alerted about "New Folder" renamed to "My Docs". So I'd like to be able to trap and ignore that first alert. Have to recognize "New Folder" though.Dispossess
What would happen if the user didn't rename it? Also, as someone else mentioned this doesn't help you if they create multiple new folders ("New Folder (2)", etc).Whelm
True, there are corner cases I can't handle. But I'd prefer to handle 80% of the cases for most users.Dispossess
Would it be possible to determine whether some file is the same as another? I mean, if there is another identification (inode?), not only the name, you have your problem solved.Subdued
R
2

Unsure if there is a more elegant way or not (I couldn't seem to find one), but those strings are stored in %windir%\System32\Shell32.dll. Theoretically, you could merely read that file in and extract the appropriate strings.

Seems a bit hacky-ish, but should get the job done. Here's a link to an article that discusses where they live in said file: http://www.askvg.com/customize-new-folder-and-new-shortcut-text-in-windows-xp/

Seems like there could or even should be an interface to them via the Windows API, but trolling through the documentation I couldn't find one. Perhaps you'll have more luck.

Retorsion answered 23/3, 2010 at 18:56 Comment(1)
I was looking at the resources too. Unfortunately the resource ID isn't stable from one version of Windows to the next :(Dispossess
U
0

FindResourceStringEx(resource_handle, new_folder_id, language_id)

returning return pwsz; generally won't work, because pwsz isn't null terminated, therefore the length is needed, but I don't know how to return 2 values, length and the pointer, so I allocate a new string..

also, I wanted to optimize away the uId / 16 and uId & 15

in the real case usage, the code will be in 2 scripts, getNumbers, useNumbers, so I put them in 2 scripts

getLocalizationNumbers.c

#include <Windows.h>
#include <stdio.h>

int main()
{
    WORD langId = 0x409; //0x409=english
    HMODULE hModule = LoadLibrary("shell32.dll");
    LPCWSTR stringToSearch = L"New Folder";
    // HMODULE hModule = LoadLibrary("shell32.dll");
    // LPCWSTR stringToSearch = L"Desktop";

    USHORT length_stringToSearch = wcslen(stringToSearch);
    for (size_t i = 1; i < 4097; ++i) {
        HRSRC hrsrc = FindResourceEx(hModule,RT_STRING,MAKEINTRESOURCE(i),langId);
        if (hrsrc) {
            HGLOBAL hglob = LoadResource(hModule, hrsrc);
            if (hglob) {
                LPCWSTR pwsz = (LPCWSTR)LockResource(hglob);
                if (pwsz) {
                    // okay now walk the string table
                    for (size_t j = 0; j < 16; j++) {
                        USHORT length = *pwsz;
                        ++pwsz;
                        if (length==length_stringToSearch && wcsncmp(pwsz, stringToSearch, length_stringToSearch)==0) {
                            printf("%llu\n", 16*(i-1)+j);
                        }
                        pwsz += length;
                    }
                }
            }
        }
    }
}

getLocalizationSpecifyLangId.c

#include <Windows.h>
#include <stdio.h>

wchar_t * getLocalizationSpecifyLangId(HMODULE hModule, UINT uId, WORD langId) {

    wchar_t *localizedString = 0;

    HRSRC hrsrc = FindResourceEx(hModule,RT_STRING,MAKEINTRESOURCE(uId/16 + 1),langId);
    if (hrsrc) {
        HGLOBAL hglob = LoadResource(hModule, hrsrc);
        if (hglob) {
            LPCWSTR pwsz = (LPCWSTR)LockResource(hglob);
            if (pwsz) {
                size_t end = uId & 15;
                for (size_t j = 0; j < end; j++) {
                    pwsz += 1 + *pwsz;
                }
                USHORT length = *pwsz;
                localizedString=(wchar_t *)malloc(sizeof(wchar_t)*(length+1));
                ++pwsz;
                memcpy(localizedString,pwsz,sizeof(wchar_t)*length);
                localizedString[length] = 0;
            }
        }
    }
    return localizedString;
}

int main()
{
    HMODULE hModule = LoadLibrary("shell32.dll");
    wprintf(L"%ls\n", getLocalizationSpecifyLangId(hModule, 4162, 0x409)); //4162="Desktop",0x409=english
    wprintf(L"%ls\n", getLocalizationSpecifyLangId(hModule, 16859, 0x409)); //16859="New Folder",0x409=english

}
Uella answered 12/3, 2023 at 18:10 Comment(0)
J
-2

If you want to handle 80% of the cases, you can start with "New Folder".

As I guess you're in an enterprise environment, you can get the folder names back into a storage, then after a week (or any time) you get through the names; update your app; release the new version which will please the users. (then later publish the results here)

You can preemptively test your app on a range of platform you're suspecting the users to use. to get a first series of folders names.

This will avoid the problem of dealing with code specific with each of the platform you're looking at.

EDIT Well I get a second thought about that, I guess you might want to warn the user about that "New Folder" if it wasn't rename after some time (say a minute)? then I guess you would need to add a list and a timer ...

Judie answered 31/3, 2010 at 14:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.