I have a project with two targets representing two different final products. Until now the localization was shared between the two targets, but now I have just one string to be localized differently according to the active target. In order to avoid duplicating the Localizable.string file ad tweak the file target membership, I decided to create two different Localizable-Ext.string files containing just the string to be translated differently for each target.
I'm using the SwiftUI Text initializer accepting a LocalizedStringKey parameter which automatically looks up the corresponding translation inside the file. This is what has to be done in most cases. I noticed that this initializer also accepts a tableName parameter corresponding to the .string filename where the match should be taken.
What I'm trying to achieve is to have a custom Text initializer which takes the string key, looks up for it inside the default Localizable.string file and, if no match is found, falls back to the right file extension (string table) and search for it there. Apparently this is tough to achieve since I cannot manage to get the key value from the LocalizedStringKey instance.
I think you need something like
extension Text {
public init<S>(looking text: S) where S : StringProtocol {
let text = String(text)
var translated = NSLocalizedString(text, comment: "")
// returns same if no translation, so ...
if translated == text {
// ... continue in other table
translated = NSLocalizedString(text, tableName: "Localizable-Ext",
bundle: .main, value: text, comment: "")
}
// already translated, so avoid search again
self.init(verbatim: translated)
}
}
Related
I have to display some sentences on a screen.
However, the language can change if the user want to, so I do not want but I can do that :
if(language==1)
{
printf("Hello sir");
}
else if(language==2)
{
printf("Hola senor");
}
OR
printf("%s",language == 1 ? "Hello sir" : "Hola senor");
I do not want that because I have a lot of iterations.
Can I use map or enum and change it during code is running, I was thinking about a things like that :
#define MESSAGE_HELLO "Hello sir" OR "Hola senor"
printf("%s",MESSAGE_HELLO);
Do you have an idea ? Can you help me please ?
You can use some internationalization library that might help you. But here I will focus on how one can solve such a problem. Naturally, you need to have a set of languages, a set of message keys and a relationship between message keys and the languages, which would hold the actual message. Let's see some solutions:
Language file
You can store english.txt, etc. which would look like this:
hello_world="Hello World"
goodbye="Good bye"
and some other language, hungarian.txt for example:
hello_world="Heló Világ"
goodbye="Viszontlátásra"
etc.
Now, you can create an array of Map<String, Map<String, String>>, loop the languages, for each language process the corresponding file and fill the map accordingly.
Database
You can store a languages table, a message_keys table and a messages table. The messages table would have a foreign key pointing to languages and another one to message_keys and the actual message would be stored there as well. You could have an API that you could use in order to request items or groups of messages in a certain language.
There are many possible solutions.
In the professional world, I have often used tables (arrays) of phrases. Each slot in the array represents the phrase in another language.
static const char str_hello[] =
{
/* English */ "hello",
/* Spanish */ "hola",
/* German */ "guten tag",
//...
};
This has worked well in embedded systems.
You probably can create two maps for the sentences you want to translate - one map for one language - and then create a pointer to one of the maps. Then use pointer and [] to take sentences you need. Also, the pointer can be changed so that it points to another map at any moment.
Finally, I found a solution that fits with my requirements.
I use a .json file with all sentences and the languages I use.
{
"my-encoding": "UTF-8",
"messages": [
{
"MESS_HELLO": {
"english": "Hello",
"spanish": "Hola"
}
},
{
"MESS_GOODBYE": {
"english": "Bye",
"spanish": "Adios"
}
},
.......
]
}
To use .json file in C++, I included json-c lib in my project.
After loading the json file, I put it in a
std::unordered_map<std::string, std::unordered_map<std::string, std::string>> m_myTable;
With an ID declared in cpp (same as MESS_HELLO or MESS_GOODBYE).
It looks like that :
//"MESS_HELLO" -> { "english" -> "Hello" ; "spanish" -> "Hola" }
//"MESS_GOODBYE" -> { "english" -> "Bye" ; "other_language" -> "mess_in_other_language" ; +++ }
With that type I can do :
Easily add a new message by changing .json file + declaring in code a new ID
Modify the text by only change .json file
Add a new language by adding to .json file + adding several lines in code, but not a big deal.
To display it, I do something like
const auto &text_it = m_myTable.find("MESS_HELLO")
const auto &language_it = text_it->second.find("english");
printf("%s",language_it->second); //print Hello
Of course, it's in functions and well implemented, but I can't tell more about that.
I have a textfield controller which matches a RegExp whenever the user type it in. For example the typed in string may be "#jack and #jill went up the hill". The following code will match the taghandles and list them.
Firstly the TextField:
TextField(
controller: myController,
)
Which listens for each input into the text field and passes it to a function:
myController.addListener(_matchTextToRegexp);
The function then matches taghandles ie. '#jack' '#jill'
_matchTextToRegexp() {
String value = myController.text;
RegExp regExpTaghandle = RegExp(r"\B#+([\w]+)\b");
Iterable matches = regExpTaghandle.allMatches(value);
matches.forEach((match) {
tagHandle = value.substring(match.start, match.end);
_callToAction(tagHandle);
}
}
The issue is that i want to call a function _callToAction() and pass it the taghandle (for example it could pass the substring/taghandle to a typeahead suggestion dropdown menu as the user types it in - similar functionality to a tweet mention). This code works for one taghandle, but if the users continues inputting text (or adds multiple taghandles) into the form it will keep matching the first taghandle even though the user has typed passed the first taghandle.
So how do you distinguish between multiple taghandles as they are dynamically typed in?
Is there any way to parametise the Datasource for the 'source' field in the Template Builder?
We have a multisite setup. As part of this it would save a lot of time and irritation if we could point our Droptrees and Treelists point at the appropriate locations rather than common parents.
For instance:
Content
--Site1
--Data
--Site2
--Data
Instead of having to point our site at the root Content folder I want to point it at the individual data folders, so I want to do something like:
DataSource=/sitecore/content/$sitename/Data
I can't find any articles on this. Is it something that's possible?
Not by default, but you can use this technique to code your datasources:
http://newguid.net/sitecore/2013/coded-field-datasources-in-sitecore/
You could possibly use relative paths if it fits with the rest of your site structure. It could be as simple as:
./Data
But if the fields are on random items all over the tree, that might not be helpul.
Otherwise try looking at:
How to use sitecore query in datasource location? (dynamic datasouce)
You might want to look at using a Querable Datasource Location and plugging into the getRenderingDatasource pipeline.
It's really going to depend on your use cases. The thing I like about this solution is there is no need to create a whole bunch of controls which effectively do he same thing as the default Sitecore ones, and you don't have to individually code up each datasource you require - just set the query you need to get the data. You can also just set the datasource query in the __standard values for the templates.
This is very similar to Holger's suggestion, I just think this code is neater :)
Since Sitecore 7 requires VS 2012 and our company isn't going to upgrade any time soon I was forced to find a Sitecore 6 solution to this.
Drawing on this article and this one I came up with this solution.
public class SCWTreeList : TreeList
{
protected override void OnLoad(EventArgs e)
{
if (!String.IsNullOrEmpty(Source))
this.Source = SourceQuery.Resolve(SContext.ContentDatabase.Items[ItemID], Source);
base.OnLoad(e);
}
}
This creates a custom TreeList control and passes it's Source field through to a class to handle it. All that class needs to do is resolve anything you have in the Source field into a sitecore query path which can then be reassigned to the source field. This will then go on to be handled by Sitecore's own query engine.
So for our multi-site solution it enabled paths such as this:
{A588F1CE-3BB7-46FA-AFF1-3918E8925E09}/$sitename
To resolve to paths such as this:
/sitecore/medialibrary/Product Images/Site2
Our controls will then only show items for the correct site.
This is the method that handles resolving the GUIDs and tokens:
public static string Resolve(Item item, string query)
{
// Resolve tokens
if (query.Contains("$"))
{
MatchCollection matches = Regex.Matches(query, "\\$[a-z]+");
foreach (Match match in matches)
query = query.Replace(match.Value, ResolveToken(item, match.Value));
}
// Resolve GUIDs.
MatchCollection guidMatches = Regex.Matches(query, "^{[a-zA-Z0-9-]+}");
foreach (Match match in guidMatches)
{
Guid guid = Guid.Parse(match.Value);
Item queryItem = SContext.ContentDatabase.GetItem(new ID(guid));
if (item != null)
query = query.Replace(match.Value, queryItem.Paths.FullPath);
}
return query;
}
Token handling below, as you can see it requires that any item using the $siteref token is inside an Site Folder item that we created. That allows us to use a field which contains the name that all of our multi-site content folders must follow - Site Reference. As long at that naming convention is obeyed it allows us to reference folders within the media library or any other shared content within Sitecore.
static string ResolveToken(Item root, string token)
{
switch (token)
{
case "$siteref":
string sRef = string.Empty;
Item siteFolder = root.Axes.GetAncestors().First(x => x.TemplateID.Guid == TemplateKeys.CMS.SiteFolder);
if (siteFolder != null)
sRef = siteFolder.Fields["Site Reference"].Value;
return sRef;
}
throw new Exception("Token '" + token + "' is not recognised. Please disable wishful thinking and try again.");
}
So far this works for TreeLists, DropTrees and DropLists. It would be nice to get it working with DropLinks but this method does not seem to work.
This feels like scratching the surface, I'm sure there's a lot more you could do with this approach.
I want to place some helper functions in another file, since they will be overly reused. I took the Computer-Databse sample's listing file:
https://github.com/playframework/Play20/blob/master/samples/scala/computer-database/app/views/list.scala.html
I created a new file, called "listing.scala.html" under the app/views package, and moved the #link function from the original file to it. This new file looks like this:
#(currentSortBy: String, currentOrder: String, currentFilter: String)
#****************************************
* Helper generating navigation links *
****************************************#
#link(newPage:Int, newSortBy:String) = #{
var sortBy = currentSortBy
var order = currentOrder
if(newSortBy != null) {
sortBy = newSortBy
if(currentSortBy == newSortBy) {
if(currentOrder == "asc") {
order = "desc"
} else {
order = "asc"
}
} else {
order = "asc"
}
}
// Generate the link
routes.Application.listPerfil(newPage, sortBy, order, currentFilter)
}
So, on my original file, I replaced the #link call, with this one:
#title
And the problem is, when I try to compile I get this error:
value link is not a member of play.api.templates.Html
But according to the documentation (http://www.playframework.org/documentation/2.0.4/ScalaTemplateUseCases) it seems to be ok.
Any guess?
Play's templates aren't the best place for placing advanced conditions, most probably you'll get better flexibility by processing it in some controller (or other method) which will return you only required link
ie.:
#title
In your case proposed link(...) function of Application controller can also return a reverse-route.
Keep in mind that including other templates is best option for repeating blocks of HTML but sometimes it's hard to get specified string (mainly because of not trimmed spaces). As you can see there is also problem with calling nested functions. Most probably you can generate whole A tag in the listing.scala.html however using it isn't comfortable enough (IMHO).
I'm storing the page content in a database table. The page content also includes some CF variables (for example "...this vendor provides services to #VARIABLES.vendorLocale#").
VARIABLES.vendorLocal is set on the page based on a URL string.
Next a CFC is accessed to get the corresponding page text from the database.
And this is then output on the page: #qryPageContent.c_content#
But #VARIABLES.vendorLocale# is showing up as is, not as the actual variable. Is there anyway to get a "variable within a variable" to be output correctly?
This is on a CF9 server.
If you have a string i.e.
variables.vendorLocal = 'foo';
variables.saveMe = 'This is a string for supplier "#variables.vendorLocal#'"' ;
WriteOutput(variables.saveMe); // This is a string for locale "foo"
then coldfusion will attempt to parse that to insert whatever variable variables.vendorLocale is. To get around this, you can use a placeholder string that is not likely to be used elsewhere. Commonly you'll see [[NAME]] used for this purpose, so in this example
variables.saveMe = 'This is a string for supplier "[[VENDORLOCALE]]'"' ;
WriteOutput(variables.saveMe); // This is a string for supplier "[[VENDORLOCALE]]"
Now you've got that you can then later on replace it for your value
variables.vendorLocal = 'bar';
variables.loadedString = Replace(variables.saveMe,'[[VENDORLOCALE]]',variables.vendorLocal);
WriteOutput(variables.loadedString); // This is a string for locale "bar"
I hope this is of help
There are lots of reasons storing code itself in the database is a bad idea, but that's not your question, so I won't go into that. One way to accomplish what you want is to take the code you have stored as as string, write a temporary file, include that file in the page, then delete that temporary file. For instance, here's a little UDF that implements that concept:
<cfscript>
function dynamicInclude(cfmlcode){
var pathToInclude = createUUID() & ".cfm";
var pathToWrite = expandPath(pathToInclude);
fileWrite(pathToWrite,arguments.cfmlcode);
include pathToInclude;
fileDelete(pathToWrite);
}
language = "CFML";
somecfml = "This has some <b>#language#</b> in it";
writeOutput(dynamicInclude(somecfml));
</cfscript>