In my application for each language string resources are stored separately and are displayed depending of type of language environment. I want to change the language in the application settings. How do I realize that after the language selection instantly apply it in the user interface?
We can use ApplicationLanguages.PrimaryLanguageOverride to change the language during runtime without restart the app.
For example: I have two languages supported "en" and "fr", localized message will show up in textblock.
Add using Windows.Globalization;
Change the default language from "en" to "fr" by
ApplicationLanguages.PrimaryLanguageOverride = "fr";
Re-navigate to the current page to refresh the UI.
Frame.Navigate(this.GetType());
Note that, you need to compare the PrimaryLanguageOverride with the system culture to set the language for next app launch, because the PrimaryLanguageOverride setting is persisted. And if you have page cache enabled, when you apply a different language on the fly, you need to clear the cache by setting Frame.CacheSize = 0; first then set it back.
Some addition to Alan Yao's answer. There's one missing piece:
After you set the Windows.Globalization.ApplicationLanguages.PrimaryLanguageOverride and before re-navigating to the current page, you must call these two functions:
Windows.ApplicationModel.Resources.Core.ResourceContext.GetForCurrentView().Reset();
Windows.ApplicationModel.Resources.Core.ResourceContext.GetForViewIndependentUse().Reset();
This way you won't need the Task.Delay() workaround mentioned by Michael Woolsey.
One more important last step: when creating a Store package, you should make sure to set the "Generate app bundle" setting value to "Never". Reason from this article:
Because otherwise, it will create a bundle. It means that he will cut
your application into different parts to optimize the download. Only
the parts that are relevant for the devices will be downloaded. For
example, if there are the assets in different resolution, it will only
download the ones that are suitable for the device. Same thing for
languages, it will only download the resources file relevant to the
language of the device. So if you try to change language, it will fall
still fall back on the same base language, because others are not
installed.
#ThisWillDoIt and #Herdo
I added a delay so that the "First" time it would work in my situation:
Windows.Globalization.ApplicationLanguages.PrimaryLanguageOverride = languageCode;
await Task.Delay(100);
Frame.Navigate(this.GetType());
Hope it helps work for you.
There is a page MSDN that describes the new feather about Language from Windows 8.1.
After modify the
ApplicationLanguages.PrimaryLanguageOverride = "en-US";
I watched the property resourceContext.Languages[0] in order to launch the PropertyChanged event of my LanguageManager, which was a StaticResources declared in App.xaml with x:Key = Loc.
private void ButtonEn_OnClick(object sender, RoutedEventArgs e)
{
ApplicationLanguages.PrimaryLanguageOverride = "en-US";
UpdateLang("en-US");
}
private async void UpdateLang(string newLang)
{
var resourceContext = Windows.ApplicationModel.Resources.Core.ResourceContext.GetForCurrentView();
while (true)
{
if (resourceContext.Languages[0] == newLang)
{
var loc = Application.Current.Resources["Loc"] as LanguagesManager;
loc.ChangeLang();
break;
}
await Task.Delay(100);
}
}
The while (true) was just for test, in fact it's better to escape by a "backup", because
Those requirements may vary depending on the UI framework used by the app, and it may be necessary to restart the app.
Unfortunately, none of the above answers helped if NavigationCacheMode set to "Required" for the page. Here's the code that solved my problem.
ApplicationLanguages.PrimaryLanguageOverride = language;
await Task.Delay(300);
Frame rootFrame = Window.Current.Content as Frame;
rootFrame.Content = null;
rootFrame = null;
rootFrame = new Frame();
rootFrame.Navigate(typeof(MainPage), null);
Window.Current.Content = rootFrame;
Related
I am creating windows phone app 8.1 in SilverLight with Visual Studio 2015. I am creating multi language app in English and Arabic. For that I have created Strings folder in the project with two folder of en-US and ar-KW with Resources.resw file in each folder.
I am x:Uid setting properties. For example Key:- Actual.Text Value:- Actual
<TextBlock x:Uid="Actual" TextWrapping="Wrap" MaxWidth="65" HorizontalAlignment="Center" />
Above is working very good. I have combobox with Item EN and AR. I am triggering SelectionChanged event to change the Language But the issue is when I stop the app and run it again than only it change the text and layout.
How can I do it at runtime without restart.
private void LanguageComboBoxName_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
string selectedItem = (e.AddedItems[0] as ComboBoxItem).Content as string;
var RootFrame = Window.Current.Content as SlideApplicationFrame;
if (selectedItem == "EN")
ApplicationLanguages.PrimaryLanguageOverride = "en-US";
else if (selectedItem == "AR")
ApplicationLanguages.PrimaryLanguageOverride = "ar-KW";
RootFrame.Navigate(this.GetType());
}
As far as i remember it is not recommended by Microsoft to change language in a runtime (by overriding it) but you can do it by reloading the page. The easiest way is to put your combobox with the code on another page and there you will override language, so when you navigate back (and page will be reloaded) you will have what you want. Keep in mind that overriding language will not localize controls in the runtime (but it shouldn't be a great issue for you). You must restart the app to localize controls.
When you override language with this code:
ApplicationLanguages.PrimaryLanguageOverride = "xx-XX";
then it is saved and you don't have to override it again on the start. App will load with the overrided language.
Additionally when you localize the app it is good to override culture info because if you have some dates - they are shown properly for the culture (e.g. 12/01/2015 or 01-12-2015)
string lang = "en-US"; //default
var culture = new CultureInfo(lang);
Windows.Globalization.ApplicationLanguages.PrimaryLanguageOverride = lang;
CultureInfo.DefaultThreadCurrentCulture = culture;
CultureInfo.DefaultThreadCurrentUICulture = culture;
There's one more important thing.
In your app manifest or what you have there, you need to set Generate app bundle to "Never". The reason is that when you uploaded packaged version to the store then your runtime localization won't work. You may be able to localize your app in the runtime with emulator or debug versions or even sometimes with deployed release versions. But you need to do that so when your app is in the store then there is no problem with localization and all of the string for languages can be accessed (because for packaged version some of them might not be if you haven't installed the language on your phone).
If you want the app to depend on a language through user selection, you'll need to store that selection and read it out on app start.
That way you can change the ApplicationLanguage when the app starts.
So in App.Xaml.cs look for the method InitializePhoneApplication() and add following lines ( or you could also try your approach here with ApplicationLanguages.PrimaryLanguageOverride )
string savedLanguage = string.Empty;
var hasSavedLanguage = AppSettings.TryGetSetting(Constants.LanguageSettingKey, out savedLanguage);
if (hasSavedLanguage)
{
Thread.CurrentThread.CurrentUICulture = new CultureInfo(savedLanguage);
Thread.CurrentThread.CurrentCulture = Thread.CurrentThread.CurrentUICulture;
}
Of course you'll also need to save the language when the user selected one from the combobox
Right now, I am trying to develop a program using Mono and GTK# on a Debian (Raspbian) system.
The issue I'm facing is, that, completely randomly, the GUI (generated by the Stetic designer or its dynamic elements) isn't completely drawn, missing either a few characters from a Label-element or whole widgets, mostly those that were dynamically created. This is how it looks on a dialog window: http://imgur.com/oEZRg7c (text is cut off)
As soon as one window shows this issue, every other window has the same issues, sometimes missing whole widgets, even if those were created afterwards. The solution is usually to quit the program and reopen it, as it only randomly occurs.
This is how the constructor of most of my windows looks like (the part after Build() varies):
public partial class ErrorSolutionDialog : Gtk.Dialog
{
public ErrorSolutionDialog (string errorMessage, string solutionHint)
{
this.WidthRequest = this.Screen.Width;
this.HeightRequest = this.Screen.Height;
this.Maximize ();
this.Fullscreen ();
this.KeepAbove = true;
this.DestroyWithParent = false;
Build ();
this.ErrorMessage.Markup = "<b><span size='xx-large'>" + errorMessage + "</span></b>";
this.SolutionHint.Text = solutionHint;
}
}
I wouldn't say that the use of the Stetic designer inside Xamarin Studio/Monodevelop is bad, but as any piece of software it certainly has some issues.
Also, the use of any designer in any software environment will tie you to that development platform forever. Finally, the created source code will be hardly maintainable, apart from completely foreign for you.
That's why I always recommend to get rid of the designer. You can follow a Gtk# tutorial such as this one, and you'll find it is easy and rewarding. And you'll have whole and thorough control of your code.
The basics about Gtk# is creating a layout with VBoxes and HBoxes. For example, the following code creates a layout in which you'll have a TreeView and a TextView in a Dialog.
var swWin1 = new Gtk.ScrollWindow();
var swWin2 = new Gtk.ScrollWindow();
// TextView
this.txtView = new Gtk.TextView();
swWin1.AddWithViewport( this.txtView );
// TreeView
this.tvView = new Gtk.TreeView();
swWin2.AddWithViewport( this.tvView );
// Layout
var hBox = new HBox( false, 2 );
hBox.PackStart( swWin1, true, true, 5 );
hBox.PackStart( swWin2, true, true, 5 );
this.VBox.PackStart( hBox, true, true, 5 );
PackStart() is the method doing the magic in order to add a widget to a layout. The booleans tell Gtk to expand the widget. A ScrollWindow adds scrollbars to any widget.
Finally, my advice is for any action, use Gtk.Action, and call its methods CreateMenuItem() and CreateToolItem() in order to create menu entries and toobar buttons, instead of repeating the same code again and again.
Hope this helps.
You can't have your cake and eat it too, apparently.
I'm currently using the System.Windows.Forms.WebBrowser in my application. The program currently depends on using the GetElementsByTagName function. I use it to gather up all the elements of a certain type (either "input"s or "textarea"s), so I can sort through them and return the value of a specific one. This is the code for that function (my WebBrowser is named web1):
// returns the value from a element.
public String FetchValue(String strTagType, String strName)
{
HtmlElementCollection elems;
HtmlDocument page = web1.Document.Window.Frames[1].Document;
elems = page.GetElementsByTagName(strTagType);
foreach (HtmlElement elem in elems)
{
if (elem.GetAttribute("name") == strName ||
elem.GetAttribute("ref") == strName)
{
if (elem.GetAttribute("value") != null)
{
return elem.GetAttribute("value");
}
}
}
return null;
}
(points to note: the webpage I need to pull from is in a frame, and depending on circumstances, the element's identifying name will be either in the name or the ref attribute)
All of that works like a dream with the System.Windows.Forms.WebBrowser.
But what it is unable to do, is redirect the opening of a new window to remain in the application. Anything that opens in a new window shoots to the user's default browser, thus losing the session. This functionality can be easily fixed with the NewWindow2 event, which System.Windows.Forms.WebBrowser doesn't have.
Now forgive me for being stunned at its absence. I have but recently ditched VB6 and moved on to C# (yes VB6, apparently I am employed under a rock), and in VB6, the WebBrowser possessed both the GetElementsByTagName function and the NewWindow2 event.
The AxSHDocVw.WebBrowser has a NewWindow2 event. It would be more than happy to help me route my new windows to where I need them. The code to do this in THAT WebBrowser is (frmNewWindow being a simple form containing only another WebBrowser called web2 (Dock set to Fill)):
private void web1_NewWindow2(
object sender,
AxSHDocVw.DWebBrowserEvents2_NewWindow2Event e)
{
frmNewWindow frmNW = new frmNewWindow();
e.ppDisp = frmNW.web2.Application;
frmNW.web2.RegisterAsBrowser = true;
frmNW.Visible = true;
}
I am unable to produce on my own a way to replicate that function with the underwhelming regular NewWindow event.
I am also unable to figure out how to replicate the FetchValue function I detailed above using the AxSHDocVw.WebBrowser. It appears to go about things in a totally different way and all my knowledge of how to do things is useless.
I know I'm a sick, twisted man for this bizarre fantasy of using these two things in a single application. But can you find it in your heart to help this foolish idealist?
I could no longer rely on the workaround, and had to abandon System.Windows.Forms.WebBrowser. I needed NewWindow2.
I eventually figured out how to accomplish what I needed with the AxWebBrowser. My original post was asking for either a solution for NewWindow2 on the System.Windows.Forms.WebBrowser, or an AxWebBrowser replacement for .GetElementsByTagName. The replacement requires about 4x as much code, but gets the job done. I thought it would be prudent to post my solution, for later Googlers with the same quandary. (also in case there's a better way to have done this)
IHTMLDocument2 webpage = (IHTMLDocument2)webbrowser.Document;
IHTMLFramesCollection2 allframes = webpage.frames;
IHTMLWindow2 targetframe = (IHTMLWindow2)allframes.item("name of target frame");
webpage = (IHTMLDocument2)targetframe.document;
IHTMLElementCollection elements = webpage.all.tags("target tagtype");
foreach (IHTMLElement element in elements)
{
if (elem.getAttribute("name") == strTargetElementName)
{
return element.getAttribute("value");
}
}
The webbrowser.Document is cast into an IHTMLDocument2, then the IHTMLDocument2's frames are put into a IHTMLFramesCollection2, then I cast the specific desired frame into an IHTMLWindow2 (you can choose frame by index # or name), then I cast the frame's .Document member into an IHTMLDocument2 (the originally used one, for convenience sake). From there, the IHTMLDocument2's .all.tags() method is functionally identical to the old WebBrowser.Document.GetElementsByTagName() method, except it requires an IHTMLElementCollection versus an HTMLElementCollection. Then, you can foreach the collection, the individual elements needing to be IHTMLElement, and use .getAttribute to retrieve the attributes. Note that the g is lowercase.
The WebBrowser control can handle the NewWindow event so that new popup windows will be opened in the WebBrowser.
private void webBrowser1_NewWindow(object sender, CancelEventArgs e)
{
// navigate current window to the url
webBrowser1.Navigate(webBrowser1.StatusText);
// cancel the new window opening
e.Cancel = true;
}
http://social.msdn.microsoft.com/Forums/en-US/csharpgeneral/thread/361b6655-3145-4371-b92c-051c223518f2/
The only solution to this I have seen was a good few years ago now, called csExWb2, now on Google code here.
It gives you an ExWebBrowser control, but with full-on access to all the interfaces and events offered by IE. I used it to get deep and dirty control of elements in a winforms-hosted html editor.
It may be a bit of a leap jumping straight into that, mind.
What am I doing:
My main intent is to enable user friendly text to speech for personal use on Win 7. Approach should work in Google Chrome, VS and Eclipse.
Code example:
Following code creates global keyboard hook for ctrl + alt + space, called hookEvent. If event fires, it starts/stops speaking clipboard contents ( that can be updated with ctrl + c ).
/// <summary>
/// KeyboardHook from: http://www.liensberger.it/web/blog/?p=207
/// </summary>
private readonly KeyboardHook hook = new KeyboardHook();
private readonly SpeechSynthesizer speaker = //
new SpeechSynthesizer { Rate = 3, Volume = 100 };
private void doSpeaking(string text)
{
// starts / stops speaking, while not blocking UI
if (speaker.State != SynthesizerState.Speaking)
speaker.SpeakAsync(text);
else
speaker.SpeakAsyncCancelAll();
}
private void hookEvent(object sender, KeyPressedEventArgs e)
{
this.doSpeaking(Convert.ToString(Clipboard.GetText()));
}
public Form1()
{
InitializeComponent();
hook.KeyPressed += new EventHandler<KeyPressedEventArgs>(hookEvent);
hook.RegisterHotKey(ModifierKeysx.Control|ModifierKeysx.Alt, Keys.Space);
}
Question:
I would prefer not using the clipboard. Or at least, restoring the value after, something like:
[MethodImpl(MethodImplOptions.Synchronized)]
private string getSelectedTextHACK()
{
object restorePoint = Clipboard.GetData(DataFormats.UnicodeText);
SendKeys.SendWait("^c");
string result = Convert.ToString(Clipboard.GetText());
Clipboard.SetData(DataFormats.UnicodeText, restorePoint);
return result;
}
What are my options?
Edit:
To my surprise, I found that my clipboard reader is the best way to go. I created a notification area app, that responds to left click (speaking clipboard) and right click (menu opens up). In menu the user can chance speed, speak or create a audio file.
MS provide accessibility tools that do cover what you're trying to do. If you take a look at documents about screen scraping. In short, every component is accessible in some manner, if you use some of the windows debugging tools you can get to see the component names/structures within. You can then use that, however, its complicated as most times you would need to be very specific for each application you intend to scrape from.
If you manage to scrape you dont need to use the clipboard, as you can access the text property of the apps direct. Its not something I've had to do, hence, Ive no code to offer off the top of my head, but the term "screen scraping" should point you in the right direction.
If to expand a little on what Bugfinder said, Microsoft provider a UI Automation Framework to solve problems like the one you mentioned:
In particular you can use the TextSelectionChangedEvent of TextPattern:
The problem with this solution is that it only works on supported operating systems and applications - and not all support this.
Your clipboard solution is acceptable for applications that do not provide a good automation interface.
But for many applications the UI Automation Framework will work well and will provide you with a far better solution.
I've been working on a pet project on the weekends to learn more about C# and have encountered an odd problem when working with localization. To be more specific, the problem I have is with System.Threading.Thread.CurrentThread.CurrentUICulture.
I've set up my app so that the user can quickly change the language of the app by clicking a menu item. The menu item in turn, saves the two-letter code for the language (e.g. "en", "fr", etc.) in a user setting called 'Language' and then restarts the application.
Properties.Settings.Default.Language = "en";
Properties.Settings.Default.Save();
Application.Restart();
When the application is started up, the first line of code in the Form's constructor (even before InitializeComponent()) fetches the Language string from the settings and sets the CurrentUICulture like so:
public Form1()
{
Thread.CurrentThread.CurrentUICulture = new CultureInfo(Properties.Settings.Default.Language);
InitializeComponent();
}
The thing is, this doesn't work consistently. Sometimes, all works well and the application loads the correct language based on the string saved in the settings file. Other times, it doesn't, and the language remains the same after the application is restarted.
At first I thought that I didn't save the language before restarting the application but that is definitely not the case. When the correct language fails to load, if I were to close the application and run it again, the correct language would come up correctly. So this implies that the Language string has been saved but the CurrentUICulture assignment in my form constructor is having no effect sometimes.
Any help? Is there something I'm missing of how threading works in C#? This could be machine-specific, so if it makes any difference I'm using Pentium Dual-Core CPU.
UPDATE
Vlad asked me to check what the CurrentThread's CurrentUICulture is. So I added a MessageBox on my constructor to tell me what the CurrentUICulture two-letter code is as well as the value of my Language user string.
MessageBox.Show(string.Format("Current Language: {0}\nCurrent UI Culture: {1}", Properties.Settings.Default.Language, Thread.CurrentThread.CurrentUICulture.TwoLetterISOLanguageName));
When the wrong language is loaded, both the Language string and CurrentUICulture have the wrong language. So I guess the CurrentUICulture has been cleared and my problem is actually with the Language Setting.
So I guess the problem is that my application sometimes loads the previously saved language string rather than the last saved language string. If the app is restarted, it will then load the actual saved language string.
I got the same problem. I figured out that Application.Restart() do not really make an absolutely restart, see MSDN.
So Application.Restart() do not call the initializing things within the forms constructor
like InitializeComponent(), more the "Applications are restarted in the context in which they were initially run."
So your code is correct
Properties.Settings.Default.Language = "en";
Properties.Settings.Default.Save();
public Form1()
{
Thread.CurrentThread.CurrentUICulture = new CultureInfo(Properties.Settings.Default.Language);
//...
InitializeComponent();
}
but it doesn't work this way with Application.Restart(). If You close the app and open it again your (new) settings are taken.
So we have to find a way to initialize the form again to make the new language settings happen.
Could you check what is your thread's CurrentUICulture?
I remember having a problem like yours; it was solved by reloading the resource dictionary containing the strings to be localized:
Thread.CurrentThread.CurrentUICulture = <new culture>;
ResourceDictionary newDict = new ResourceDictionary();
newDict.Source = localizedStrings.Source;
localizedStrings = newDict;
(and this approach worked dynamically as well; here is some more information).
You could manually change the language of the current form similar to this:
CultureInfo cInfo = new CultureInfo("en-US");
ResourceManager rm = new ResourceManager(GetType());
// For each control on the form, perform the translation manually (probably better in a loop)
control1.Text = rm.GetString(control1.Name + ".Text", cInfo);
// Now set the culture for all other dialogs
Thread.CurrentThread.CurrentUICulture = cInfo;
Hope that helps!
You could simply "reset" your application by closing the open forms and re-creating them. Then you could directly set the culture when the user changes the setting.
Also, try giving some debug output so you see what values are being set and if the culture is actually what you expect.
EDIT: My guess: Since the data has to be written to a file, and then loaded from that file, you may be restarting too quickly for the write to have been completed.
By using CurrentThread.CurrentUICulture, and then changing the form, you don't need to restart the application. Ref my old post here