12 ms·
The idea that a translation string can execute Python code via ${expression} is really quite scary. Imagine a user downloading a third-party localization file t
by byuu 11y ago
The idea that a translation string can execute Python code via ${expression} is really quite scary. Imagine a user downloading a third-party localization file that ends up being malicious.
Overall, I see there being two major styles for building strings. Since I use C++, I can't do literal interpolation, but I can get close with variadic templates. I hope you guys won't me talking about a statically typed language here, but it should be on topic to string interpolation at least.
//interpolation
print("My name is ", name, " and next year, I will be ", age + 1, " years old.\n");
//arguments
print("My name is {0}, and next year, I will be {1} years old.\n", name, age + 1);
//arguments with hinting (:label is ignored)
print("My name is {0:name}, and next year, I will be {1:age} years old."\n", fetch(), *values + 1);
print(hex(size, 4), " KiB"); //interpolation formatting
print("{0,4x} KiB", size); //argument formatting (more covenient, but more cryptic and limited)
//without the print component:
string s{"My name is: ", name, "\n"}; //interpolation
string s{"My name is: {0}\n", name}; //arguments
The latter is certainly vastly superior for localization. Because in another language, it may be more grammatically correct to reverse the parameters, eg: "来年の歳は{1}と名前は{0}"; which is something C printf doesn't support, shy of unofficial extensions for argument reordering.
But I find that as the string gets longer and longer, the former is much more readable. (You'll have to imagine the syntax highlighting, which helps a lot.) Eg when you have 17 arguments, it can be annoying to count to find the 11th parameter, whereas with interpolation, it's right there where it's used.
Ultimately, I decided on the former being the default, and added a format class for the latter, which is also really handy for building your argument lists somewhere else. Plus the default won't clobber your escape characters (in my case, braces.)
//inline format
print("I am {0}, and I'm {1} years old.\n", format{name, age});
//external format
format f;
f.append(fetch()); //person's age
f.append(*values + 1); //person's name
... //you could fetch the string from a translation table below, too
print("I am {0}, and I'm almost {1} years old.\n", f);
Whenever string::append(format) is invoked, it will use all of the values inside the format array to parse {#} entries in the string. Of course, it's also safe and will simply skip over a reference to {2} in a format array with only one value.
Curious to hear others' opinions on the above, and any suggestions for improvement.