Xamarin iOS localization using .NET - c#

I'm trying to use .NET localization in a portable class library for a Xamarin iOS/Android project. I've followed the steps at:
How to use localization in C#
And have code which looks as follows:
string sText = strings.enter_movie_name;
Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo("fr");
sText = strings.enter_movie_name;
lblEnterMovieName.Text = sText;
I've also tried:
ResourceManager resman = new ResourceManager(typeof(MyApplication.strings));
string sText = resman.GetString("enter_movie_name", new CultureInfo("fr"));
I've created strings.resx with enter_movie_name equal to "Enter movie name:" and strings.fr.resx equal to "Entre la movie name:"
However, sText always ends up as "Enter movie name:". I can't seem to get the "Entre la movie name:" version out.
I also saw the post at Cross-platform Localization but couldn't work it out. I also don't want to use the iOS specific version at Localization on Xamarin.iOS as I'd like to be able to get at my strings from a platform independent library.
Can anyone point out where I'm going wrong?

I've created a bit of an ugly solution, but it works. What I've done is made a folder called 'strings' in my Portable Class Library (PCL) and inside that created files called:
strings.resx
strings_fr.resx
strings_ja_JP.resx
I've set all of these as Embedded Resource with custom tool as ResXFileCodeGenerator. This means I have a separate resource DLL for each language.
In iOS I can then get the locale by calling:
string sLocale = NSLocale.CurrentLocale.LocaleIdentifier;
I would guess there's an Android equivalent using Locale but I don't know what it is.
This gives me a string like "ja_JP" or "fr_FR" or "en_GB" (note they're underscores, not dashes). I then use this with the following static class I created to retrieve an appropriate resource manager and get the string from it.
If given a locale aa_BB it first looks for strings_aa_BB, then for strings_aa, then for strings.
public static class Localise
{
private const string STRINGS_ROOT = "MyPCL.strings.strings";
public static string GetString(string sID, string sLocale)
{
string sResource = STRINGS_ROOT + "_" + sLocale;
Type type = Type.GetType(sResource);
if (type == null)
{
if (sLocale.Length > 2) {
sResource = STRINGS_ROOT + "_" + sLocale.Substring(0, 2); // Use first two letters of region code
type = Type.GetType(sResource);
}
}
if (type == null) {
sResource = STRINGS_ROOT;
type = Type.GetType(sResource);
if (type == null)
{
System.Diagnostics.Debug.WriteLine("No strings resource file when looking for " + sID + " in " + sLocale);
return null; // This shouldn't ever happen in theory
}
}
ResourceManager resman = new ResourceManager(type);
return resman.GetString(sID);
}
}
An example of how this would be used (referring to the above code) would be:
string sText = Localise.GetString("enter_movie_name", sLocale);
lblEnterMovieName.Text = sText;
A significant downside of this is that all views will need to have their text set programatically, but does have the upside that the translations can be done once and then reused on many platforms. They also remain separate from the main code in their own DLLs and therefore can be recompiled individually if necessary.

I created a similar solution to the accepted answer but using txt files instead of Resx and nuget ready to go: https://github.com/xleon/I18N-Portable. Blog post here.
Other improvements are:
"anyKey".Translate(); // from anywhere
Automatic detection of the current culture
Get a list of supported translations: List<PortableLanguage> languages = I18N.Current.Languages;
Support for Data Binding in Mvvm frameworks / Xamarin.Forms
etc

Related

How to create a shared parameter that can be shared when project standards are transferred

I am working on a project where I need to create multiple revit files consisting of wall types and create shared parameters into them. I completed this process.
But on manually clicking on Manage > Transfer Project Standards
Copy from "project name" > Wall Types through the revit interface.
I imported the wall types of different revit files created into one.
But the shared parameters seems to repeat in the type parameter list of the wall type with data in one set and the repeated set has no data.
It looks like the parameters I created are not shareable.
if (Convert.ToString(value) != "")
{
Type type = value.GetType();
string originalFile = uiApp.Application.SharedParametersFilename;
string tempFile = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()) + ".txt";
using (File.Create(tempFile)) { }
uiApp.Application.SharedParametersFilename = tempFile;
try
{
if (ele.LookupParameter(param) == null)
{
ExternalDefinitionCreationOptions edco = null;
if (type.Name.Equals("Double"))
edco = new ExternalDefinitionCreationOptions(param, ParameterType.Number);
else
edco = new ExternalDefinitionCreationOptions(param, ParameterType.Text);
edco.Visible = true;
var definition = uiApp.Application.OpenSharedParameterFile().Groups.Create("Custom Parameters").Definitions.Create(edco);
var newCategorySet = uiApp.Application.Create.NewCategorySet();
newCategorySet.Insert(doc.Settings.Categories.get_Item(BuiltInCategory.OST_Walls));
Autodesk.Revit.DB.Binding binding = uiApp.Application.Create.NewTypeBinding(newCategorySet);
doc.ParameterBindings.Insert(definition, binding, BuiltInParameterGroup.PG_IDENTITY_DATA);
if (!string.IsNullOrEmpty((string)value))
ele.LookupParameter(param).Set((string)value);
}
else
{
if (!string.IsNullOrEmpty((string)value))
ele.LookupParameter(param).Set((string)
}
}
catch (Exception ex)
{
}
finally
{
uiApp.Application.SharedParametersFilename = originalFile;
}
}
Here, this block of code is executed in a loop where "element" is the element into whom the shared parameter needs to be added. "param" is the parameter name and "value" is the value for the parameter. Please let me know if this is the right way to create shared parameter that can be shared when project wall types are transferred to another project.
Thank you
definitely this will happen, as long as you are in a loop, and keeps creating a shared parameter for each file. this will lead to creating unique GUID for each revit file. and when you combine all you will find all the shared parameters with different Guids but with the same name.
you need to create the shared parameter once, then for each revit file, set the sharedparameter file (that is already created with sharedparameter) and get the sharedparameter from it, then assign it to the category you wish for each revit file.
moreinfo about shared parameters here
hope that helps.

Visual Studio Debugger Extension get user settings

I'm writing a visual studio extension based on the Concord Samples Hello World project. The goal is to let the user filter out stack frames by setting a list of search strings. If any of the search strings are in a stack frame, it is omitted.
I've got the filter working for a hardcoded list. That needs to be in a non-package-based dll project in order for the debugger to pick it up. And I have a vsix project that references that dll with an OptionPageGrid to accept the list of strings. But I can't for the life of me find a way to connect them.
On the debugger side, my code looks something like this:
DkmStackWalkFrame[] IDkmCallStackFilter.FilterNextFrame(DkmStackContext stackContext, DkmStackWalkFrame input)
{
if (input == null) // null input frame indicates the end of the call stack. This sample does nothing on end-of-stack.
return null;
if (input.InstructionAddress == null) // error case
return new[] { input };
DkmWorkList workList = DkmWorkList.Create(null);
DkmLanguage language = input.Process.EngineSettings.GetLanguage(new DkmCompilerId());
DkmInspectionContext inspection = DkmInspectionContext.Create(stackContext.InspectionSession, input.RuntimeInstance, input.Thread, 1000,
DkmEvaluationFlags.None, DkmFuncEvalFlags.None, 10, language, null);
string frameName = "";
inspection.GetFrameName(workList, input, DkmVariableInfoFlags.None, result => GotFrameName(result, out frameName));
workList.Execute();
CallstackCollapserDataItem dataItem = CallstackCollapserDataItem.GetInstance(stackContext);
bool omitFrame = false;
foreach (string filterString in dataItem.FilterStrings)
{
if (frameName.Contains(filterString))
{
omitFrame = true;
}
}
The CallstackCollapserDataItem is where I theoretically need to retrieve the strings from user settings. But I don't have access to any services/packages in order to e.g. ask for WritableSettingsStore, like in You've Been Haacked's Example. Nor can I get my OptionPageGrid, like in the MSDN Options Example.
The other thing I tried was based on this StackOverflow question. I overrode the LoadSettingsFromStorage function of my OptionPageGrid and attempted to set a static variable on a public class in the dll project. But if that code existed in the LoadSettingsFromStorage function at all, the settings failed to load without even entering the function. Which felt like voodoo to me. Comment out the line that sets the variable, the breakpoint hits normally, the settings load normally. Restore it, and the function isn't even entered.
I'm at a loss. I really just want to pass a string into my Concord extension, and I really don't care how.
Ok, apparently all I needed to do was post the question here for me to figure out the last little pieces. In my CallstackCollapserDataItem : DkmDataItem class, I added the following code:
private CallstackCollapserDataItem()
{
string registryRoot = DkmGlobalSettings.RegistryRoot;
string propertyPath = "vsix\\CallstackCollapserOptionPageGrid";
string fullKey = "HKEY_CURRENT_USER\\" + registryRoot + "\\ApplicationPrivateSettings\\" + propertyPath;
string savedStringSetting = (string)Registry.GetValue(fullKey, "SearchStrings", "");
string semicolonSeparatedStrings = "";
// The setting resembles "1*System String*Foo;Bar"
if (savedStringSetting != null && savedStringSetting.Length > 0 && savedStringSetting.Split('*').Length == 3)
{
semicolonSeparatedStrings = savedStringSetting.Split('*')[2];
}
}
vsix is the assembly in which CallstackCollapserOptionPageGrid is a DialogPage, and SearchStrings is its public property that's saved out of the options menu.

How to Check a input text is Norwegian language or not in c# [duplicate]

What's the best way to detect the language of a string?
If the context of your code have internet access, you can try to use the Google API for language detection.
http://code.google.com/apis/ajaxlanguage/documentation/
var text = "¿Dónde está el baño?";
google.language.detect(text, function(result) {
if (!result.error) {
var language = 'unknown';
for (l in google.language.Languages) {
if (google.language.Languages[l] == result.language) {
language = l;
break;
}
}
var container = document.getElementById("detection");
container.innerHTML = text + " is: " + language + "";
}
});
And, since you are using c#, take a look at this article on how to call the API from c#.
UPDATE:
That c# link is gone, here's a cached copy of the core of it:
string s = TextBoxTranslateEnglishToHebrew.Text;
string key = "YOUR GOOGLE AJAX API KEY";
GoogleLangaugeDetector detector =
new GoogleLangaugeDetector(s, VERSION.ONE_POINT_ZERO, key);
GoogleTranslator gTranslator = new GoogleTranslator(s, VERSION.ONE_POINT_ZERO,
detector.LanguageDetected.Equals("iw") ? LANGUAGE.HEBREW : LANGUAGE.ENGLISH,
detector.LanguageDetected.Equals("iw") ? LANGUAGE.ENGLISH : LANGUAGE.HEBREW,
key);
TextBoxTranslation.Text = gTranslator.Translation;
Basically, you need to create a URI and send it to Google that looks like:
http://ajax.googleapis.com/ajax/services/language/translate?v=1.0&q=hello%20worled&langpair=en%7ciw&key=your_google_api_key_goes_here
This tells the API that you want to translate "hello world" from English to Hebrew, to which Google's JSON response would look like:
{"responseData": {"translatedText":"שלום העולם"}, "responseDetails": null, "responseStatus": 200}
I chose to make a base class that represents a typical Google JSON response:
[Serializable]
public class JSONResponse
{
public string responseDetails = null;
public string responseStatus = null;
}
Then, a Translation object that inherits from this class:
[Serializable]
public class Translation: JSONResponse
{
public TranslationResponseData responseData =
new TranslationResponseData();
}
This Translation class has a TranslationResponseData object that looks like this:
[Serializable]
public class TranslationResponseData
{
public string translatedText;
}
Finally, we can make the GoogleTranslator class:
using System;
using System.Collections.Generic;
using System.Text;
using System.Web;
using System.Net;
using System.IO;
using System.Runtime.Serialization.Json;
namespace GoogleTranslationAPI
{
public class GoogleTranslator
{
private string _q = "";
private string _v = "";
private string _key = "";
private string _langPair = "";
private string _requestUrl = "";
private string _translation = "";
public GoogleTranslator(string queryTerm, VERSION version, LANGUAGE languageFrom,
LANGUAGE languageTo, string key)
{
_q = HttpUtility.UrlPathEncode(queryTerm);
_v = HttpUtility.UrlEncode(EnumStringUtil.GetStringValue(version));
_langPair =
HttpUtility.UrlEncode(EnumStringUtil.GetStringValue(languageFrom) +
"|" + EnumStringUtil.GetStringValue(languageTo));
_key = HttpUtility.UrlEncode(key);
string encodedRequestUrlFragment =
string.Format("?v={0}&q={1}&langpair={2}&key={3}",
_v, _q, _langPair, _key);
_requestUrl = EnumStringUtil.GetStringValue(BASEURL.TRANSLATE) + encodedRequestUrlFragment;
GetTranslation();
}
public string Translation
{
get { return _translation; }
private set { _translation = value; }
}
private void GetTranslation()
{
try
{
WebRequest request = WebRequest.Create(_requestUrl);
WebResponse response = request.GetResponse();
StreamReader reader = new StreamReader(response.GetResponseStream());
string json = reader.ReadLine();
using (MemoryStream ms = new MemoryStream(Encoding.Unicode.GetBytes(json)))
{
DataContractJsonSerializer ser =
new DataContractJsonSerializer(typeof(Translation));
Translation translation = ser.ReadObject(ms) as Translation;
_translation = translation.responseData.translatedText;
}
}
catch (Exception) { }
}
}
}
Fast answer: NTextCat (NuGet, Online Demo)
Long answer:
Currently the best way seems to use classifiers trained to classify piece of text into one (or more) of languages from predefined set.
There is a Perl tool called TextCat. It has language models for 74 most popular languages. There is a huge number of ports of this tool into different programming languages.
There were no ports in .Net. So I have written one: NTextCat on GitHub.
It is pure .NET Framework DLL + command line interface to it. By default, it uses a profile of 14 languages.
Any feedback is very appreciated!
New ideas and feature requests are welcomed too :)
Alternative is to use numerous online services (e.g. one from Google mentioned, detectlanguage.com, langid.net, etc.).
A statistical approach using digraphs or trigraphs is a very good indicator. For example, here are the most common digraphs in English in order: http://www.letterfrequency.org/#digraph-frequency (one can find better or more complete lists). This method may have a better success rate than word analysis for short snippets of text because there are more digraphs in text than there are complete words.
If you mean the natural (ie human) language, this is in general a Hard Problem. What language is "server" - English or Turkish? What language is "chat" - English or French? What language is "uno" - Italian or Spanish (or Latin!) ?
Without paying attention to context, and doing some hard natural language processing (<----- this is the phrase to google for) you haven't got a chance.
You might enjoy a look at Frengly - it's a nice UI onto the Google Translate service which attempts to guess the language of the input text...
Make a statistical analyses of the string: Split the string into words. Get a dictionary for every language you want to test for. And then find the language that has the highest word count.
In C# every string in memory will be unicode, and is not encoded. Also in text files the encoding is not stored. (Sometimes only an indication of 8-bit or 16-bit).
If you want to make a distinction between two languages, you might find some simple tricks. For example if you want to recognize English from Dutch, the string that contains the "y" is mostly English. (Unreliable but fast).
CLD3 (Compact Language Detector v3) library from Google's Chromium browser
You could wrap the CLD3 library, which is written in C++.
We can use Regex.IsMatch(text, "[\\uxxxx-\\uxxxx]+") to detect an specific language. Here xxxx is the 4 digit Unicode id of a character.
To detect Arabic:
bool isArabic = Regex.IsMatch(yourtext, #"[\u0600-\u06FF]+")
You may use the C# package for language identification from Microsoft Research:
This package implements several algorithms for language
identification, and includes two sets of pre-compiled language
profiles. One set covers 52 languages and was trained on Wikipedia
(i.e. a well-written corpus); the other covers 26 languages and was
constructed from Twitter (i.e. a highly colloquial corpus). The
language identifiers are packaged up as a C# library, and be easily
embedded into other C# projects.
Download the package from the above link.
One alternative is to use 'Translator Text API' which is
... part of the Azure Cognitive Services API collection of machine
learning and AI algorithms in the cloud, and is readily consumable in
your development projects
Here's a quickstart guide on how to detect language from text using this API

how to show publish version in a textbox?

At the moment I am manually updating the version field (textbox) in my application every time I publish it. I am wondering if there is a way to have my application get that data from somewhere and display it in the box for me. I am using VS2012 and I am just unsure of how to achieve that in C#. Below is a screenshot of the VS2012 properties window that I am talking about.
NEW IMAGE:
Don't forget to check if the application is networkdeployed otherwise it won't work in debug mode.
if (ApplicationDeployment.IsNetworkDeployed)
{
this.Text = string.Format("Your application name - v{0}",
ApplicationDeployment.CurrentDeployment.CurrentVersion.ToString(4));
}
Try this:
using System.Deployment.Application;
public Version AssemblyVersion
{
get
{
return ApplicationDeployment.CurrentDeployment.CurrentVersion;
}
}
Then the caller to the getter property can de-reference the Major, Minor, Build and Revision properties, like this:
YourVersionTextBox.Text = AssemblyVersion.Major.ToString() + "."
+ AssemblyVersion.Minor.ToString() + "."
+ AssemblyVersion.Build.ToString() + "."
+ AssemblyVersion.Revision.ToString();
Also we can use overloadedToString of System.Version
using System.Deployment.Application;
public Version AssemblyVersion
{
get
{
return ApplicationDeployment.CurrentDeployment.CurrentVersion;
}
}
YourVersionTextBox.Text = AssemblyVersion.ToString(1); // 1 - get only major
YourVersionTextBox.Text = AssemblyVersion.ToString(2); // 1.0 - get only major, minor
YourVersionTextBox.Text = AssemblyVersion.ToString(3); // 1.0.3 - get only major, minor, build
YourVersionTextBox.Text = AssemblyVersion.ToString(4); // 1.0.3.4 - get only major, minor, build, revision
Method 1 :
You can use this
string version = Application.ProductVersion;
and show the version in your textBox.
Method 2 :
or if you want the version parts separately, you can use this :
System.Version version2 = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
now you have these :
version2.Major;
version2.Minor;
version2.Revision;
version2.Build;
and you can use them like this
string versionString= (String.Format("{0}.{1}.{2}.{3}", version2.Major, version2.Minor, version2.Revision, version2.Build));
If you get an error of ApplicationDeployment, then you can try this:
For global version accessing through Program, declare inside Program class:
private static Version version = new Version(Application.ProductVersion);
public static Version Version
{
get
{
return version;
}
}
Now you have Program.Version anywhere available in the program and you can use it to get version information like this:
LabelVersion.Text = String.Format("Version {0}.{1}",
Program.Version.Major.ToString(),
Program.Version.Minor.ToString());

Navigating to DNN Module

I'm forming a newsletter with links to various html modules within my DNN website. I have access to each of their ModuleID's and I'm wanting to use that to get the url. The current approach (made by a third party developer) worked, but only to a degree. The url's are incorrectly formed when the Modules are located deeper in the website.
For example module located at www.website.com/website/articles.aspx is works fine, but a module located www.website.com/website/articles/subarticles.aspx won't. I know this is because the url is incorrectly formed.
Here's the current code:
DotNetNuke.Entities.Modules.ModuleController objModCtrlg = new DotNetNuke.Entities.Modules.ModuleController();
DotNetNuke.Entities.Modules.ModuleInfo dgfdgdg = objModCtrlg.GetModule(ContentMID);
TabController objtabctrll = new TabController();
TabInfo objtabinfoo = objtabctrll.GetTab(tabidfrcontent);
string tabnamefremail= objtabinfoo.TabName;
moduletitlefrEmail = dgfdgdg.ModuleTitle;
string readmorelinkpath = basePath + "/" + tabnamefremail + ".aspx";
ContentMID is the current module ID I'm looking at. I've tried to use Globals.NavigateURL, but that always crashes with Object reference not set to an instance of an object. error. Same thing when I use objtabinfoo.FullUrl so I'm currently at a loss as to how I get the specific modules URL.
EDIT: Here's some more code as to how the tabId is retrieved.
IDictionary<int, TabInfo> dicTabInfo12 = new Dictionary<int, TabInfo>();
ContentMID = Convert.ToInt32(dsNewsList.Tables[0].Rows[i]["ModuleID"]);
dicTabInfo12 = objTabctrl.GetTabsByModuleID(ContentMID);
if (dicTabInfo12.Count > 0)
{
string tester = ""; //Debug
foreach (KeyValuePair<int, TabInfo> item1 in dicTabInfo12)
{
tabidfrcontent = item1.Key;
}
}
You really should be using NavigateUrl to build the links ance if you have the tabid, you are golden.
string readMoreLinkPath = NavigateUrl(tabidfrcontent);
Nice and simple
Okay, colleague suggested this and it works great within a scheduler.
string linkPath = basePath + "/Default.aspx?TabID=" + tabID;
Will Navigate you to the correct tab ID. So this would be the best solution if you're forced to work within a scheduler where you can't use NavigateUrl without some major workarounds.

Categories