This is exactly why this thread exists. In the code I use the rules for each language to look up special strings when a number is involved.
I have a way to specify the rules in code (and the rules for Russian are already there), exactly as you mention them.
If the special treatment is asked for a key based on a number n (eg STR_SOLDIERS_IN_CRAFT), the following are tried:
If n is 0 the STR_SOLDIERS_IN_CRAFT_0 is selected, if it exists. This happens for all languages.
Based on each language different rules are used, and one of keys STR_SOLDIERS_IN_CRAFT_1 to _K are selected, based on the language. _1, _2 and the rest may have different meaning for each language, they are NOT the number n. It is simply the k-form in the rules for the specific language. So, while English and French both use _1 and _2, for English _1 is used only for singular and _2 is used for plural and for zero (if _0 is not found). For French _1 is used for singular and as the zero fallback, while _2 is used for plural.
Please check the rules in the source code (file src/Engine/Language.cpp) it should be easy enough to understand the various "getSuffix" functions for the different languages.
The problem is that a lot of the strings are created from smaller pieces (eg words) and thus the actual game code needs to change to use the new translation facility and make sentenses that the translators can modify. As a fictional example:
Now the code is like:
message = translate("STR_THERE_ARE") + n + translate(n == 1 ? "STR_SOLDIER" : "STR_SOLDIERS") + translate("STR_IN_THE_CRAFT")
and the translators only see the "STR_" strings.
It should be changed to
message = translate("STR_THERE_ARE_N_SOLDIERS_IN_CRAFT", n)
and then the translator for (Rusian?) will see:
STR_THERE_ARE_N_SOLDIERS_IN_CRAFT_0
Some rusian for żołnierzy or sztuk
STR_THERE_ARE_N_SOLDIERS_IN_CRAFT_1
maybe {N} sztuka or {N} żołnierz
STR_THERE_ARE_N_SOLDIERS_IN_CRAFT_2
maybe some {N} sztuki or {N} żołnierzy
STR_THERE_ARE_N_SOLDIERS_IN_CRAFT_3
maybe some {N} sztuk or {N} żołnierzy
while the translators for French will see:
STR_THERE_ARE_N_SOLDIERS_IN_CRAFT_0
French for no soldiers in craft
STR_THERE_ARE_N_SOLDIERS_IN_CRAFT_1
French for a single soldier in craft
STR_THERE_ARE_N_SOLDIERS_IN_CRAFT_2
French for {N} soldiers in craft
And the English text will be:
STR_THERE_ARE_N_SOLDIERS_IN_CRAFT_0
No soldiers in craft
STR_THERE_ARE_N_SOLDIERS_IN_CRAFT_1
A single soldier in craft
STR_THERE_ARE_N_SOLDIERS_IN_CRAFT_2
{N} soldiers in craft
And if the _0 form is missing in English, the _2 form will be tried, but for French the _1 form will be tried.