I am trying to use .resx files for localization in xamarin.mac
Inside AppDelegate I changed current culture of the thread :
System.Threading.Thread.CurrentThread.CurrentUICulture = new CultureInfo ("ru");
System.Threading.Thread.CurrentThread.CurrentCulture = new CultureInfo ("ru");
also I have two resource files inside my app:
But strings are always shown from the default resource file... Any solution?
I also use native language change, here is whole AppDelegate constructor :
public AppDelegate ()
{
string [] lang = { "ru", "en" };
NSUserDefaults.StandardUserDefaults.SetValueForKey (NSArray.FromObjects (lang), (Foundation.NSString)"AppleLanguages");
NSUserDefaults.StandardUserDefaults.Synchronize ();
System.Threading.Thread.CurrentThread.CurrentUICulture = new CultureInfo ("ru");
System.Threading.Thread.CurrentThread.CurrentCulture = new CultureInfo ("ru");
Resources.Culture = GetCurrentCultureInfo ();
}
BTW , xamarin studio 6.1.1 (build 15) doesn't allow me to add resource.ru.resx if I have resource.ru in my project , kinda bug !
I have created resource-ru.resx file and than renamed it .
You need to set the app culture after retrieving it from the OS, something like this (warning, this is from Xamarin iOS, but for the documentation it works the same way, if not advice me and I will delete the post):
//Add this somewhere
public System.Globalization.CultureInfo GetCurrentCultureInfo ()
{
var netLanguage = "en";
if (NSLocale.PreferredLanguages.Length > 0) {
var pref = NSLocale.PreferredLanguages [0];
netLanguage = pref.Replace ("_", "-"); // turns en_US into en-US
}
try
{
return new System.Globalization.CultureInfo(netLanguage);
}catch{
try{
return new System.Globalization.CultureInfo(netLanguage.Substring(0, netLanguage.IndexOf("-")));
}
catch{
return new System.Globalization.CultureInfo("en");
}
}
}
//And finally add this at the start of the app
AppResources.Culture = GetCurrentCultureInfo();
Likely you are running into
https://bugzilla.xamarin.com/show_bug.cgi?id=45696
You can work around it using a custom msbuild step. Shown here (due to length):
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<CreateAppBundleDependsOn>$(CreateAppBundleDependsOn);WorkAroundLocalizationBug-ES;WorkAroundLocalizationBug-FR</CreateAppBundleDependsOn>
</PropertyGroup>
<Target Name="WorkAroundLocalizationBug-ES" Inputs="$(OutputPath)es/XMLocalizationSample.resources.dll" Outputs="$(AppBundleDir)/Contents/MonoBundle/es/XMLocalizationSample.resources.dll">
<Copy SourceFiles="$(OutputPath)es/XMLocalizationSample.resources.dll" DestinationFiles="$(AppBundleDir)/Contents/MonoBundle/es/XMLocalizationSample.resources.dll" />
</Target>
<Target Name="WorkAroundLocalizationBug-FR" Inputs="$(OutputPath)fr/XMLocalizationSample.resources.dll" Outputs="$(AppBundleDir)/Contents/MonoBundle/fr/XMLocalizationSample.resources.dll">
<Copy SourceFiles="$(OutputPath)fr/XMLocalizationSample.resources.dll" DestinationFiles="$(AppBundleDir)/Contents/MonoBundle/fr/XMLocalizationSample.resources.dll" />
</Target>
</Project>
A full sample can be found here: https://github.com/xamarin/mac-samples/tree/master/XMLocalizationSample
Related
I create a can edit ico file application, I publish with ClickOnce.
I want to click ico file to open my winform application, but my application can't get args(file path).
I try: string fileName = AppDomain.CurrentDomain.SetupInformation.ActivationArguments.ActivationData[0];
but ide show error message: Cannot resolve symbol 'ActivationArguments'
on my ClickOnceProfile.pubxml file:
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<FileAssociation Include=".ico">
<Visible>False</Visible>
<Description>ico</Description>
<Progid>ico</Progid>
<DefaultIcon>Resources\ico.ico</DefaultIcon>
</FileAssociation>
</ItemGroup>
</Project>
BTY, if I use this code, I can get arg (not use ClickOnce), but I want to use ClickOnce, please help me.
[STAThread]
static void Main(string[]? args)
{
Application.Run(new Form1(args));
}
public Form1(string[]? args)
{
string filepath = args[0];
}
relevant information:
.NET 6
windows 11
windows forms
Step-1:
Update the VS2022 to the lastest version.
Step-2:
Project RightClick => Publish => Publish-Tab => Show All => Setting-tab => Options => Allow Url-Paramaters....
Step-3:
Copy this file code to you app.
Step-4:
Write Fllowing code:
var clickOnceInfo = new ClickOnceInfo();
if (!clickOnceInfo.IsNetworkDeployed)
{
MessageBox.Show("设计器只能从Web页面启动,无法独立使用");
return;
}
var query = clickOnceInfo.ActivationUri?.Query ?? "QueryEMPTY";
if (query == "QueryEMPTY")
{
MessageBox.Show("参数设置异常,无法启动设计器");
return;
}
I am working on adding localisation to my class library. Currently I have two resource files: Strings.resx and Strings.es.resx.
Both files are under the 'internal' access modifier, although I have tried setting both to 'public' without any help.
My problem is that the Spanish resource file (Strings.es.resx) is not being loaded; and this problem will repeat with any more resource files I add for other languages. The Strings.resx works fine as it is the default resource file.
This code is used to grab which string resource files have been loaded; currently only the default file is loaded. Spanish does not appear:
private static void LoadLanguages()
{
var cultures = CultureInfo.GetCultures(CultureTypes.AllCultures);
foreach (var culture in cultures)
{
try
{
var rs = Properties.Lang.Strings.ResourceManager.GetResourceSet(culture, true, false);
if (rs != null) SupportedLanguages.Add(culture.Name.ToLower(), culture.NativeName);
}
catch (Exception)
{
// ignored
}
}
Log.Info("Loaded languages: " + SupportedLanguages.Count); //OUT: 1
}
I have made a discovery though. In my build output, there is a folder "es", and within that folder is a DLL called Project.resources.dll. If I copy that DLL to the root folder of the build output, the resource gets loaded.
The solution to this problem is to get those resource files loaded from the folders. For some reason this is not happening. Is there a known solution to this? Thanks.
It works out the threads current culture. An example can be seen in the docs over at Microsoft https://learn.microsoft.com/en-us/dotnet/framework/resources/creating-satellite-assemblies-for-desktop-apps (check code at step 13 in the end)
Below the example from the documentation. The localized resource is StringLibrary
using System;
using System.Globalization;
using System.Threading;
public class Example
{
public static void Main()
{
string[] cultureNames = { "en-GB", "en-US", "fr-FR", "ru-RU" };
Random rnd = new Random();
string cultureName = cultureNames[rnd.Next(0, cultureNames.Length)];
Thread.CurrentThread.CurrentUICulture = CultureInfo.CreateSpecificCulture(cultureName);
Console.WriteLine("The current UI culture is {0}",
Thread.CurrentThread.CurrentUICulture.Name);
StringLibrary strLib = new StringLibrary();
string greeting = strLib.GetGreeting();
Console.WriteLine(greeting);
}
}
I have been trying to write an MsBuild task to automatically get Nuget packages from a feed url and automatically update the packages.config to update to the latest version.
// ---- Download and install a package at a desired path ----
var sourceUri = new Uri("FEED URL");
// ---- Update the ‘packages.config’ file ----
var packageReferenceFile = new PackageReferenceFile("../../packages.config");
string packagesPath = "../../packages";
IPackageRepository sourceRepository = PackageRepositoryFactory.Default.CreateRepository(sourceUri.ToString());
PackageManager packageManager = new PackageManager(sourceRepository, packagesPath);
foreach (var sourcePackage in sourceRepository.GetPackages().Where(x => x.IsLatestVersion))
{
if (!packageReferenceFile.EntryExists(sourcePackage.Id + " " + sourcePackage.Version, sourcePackage.Version))
{
var oldPackage = packageReferenceFile.GetPackageReferences().FirstOrDefault(x => x.Id.Contains(sourcePackage.Id));
if (oldPackage != null)
{
packageReferenceFile.DeleteEntry(oldPackage.Id, oldPackage.Version);
}
packageManager.InstallPackage(sourcePackage.Id, SemanticVersion.Parse(sourcePackage.Version.ToFullString()));
// Get the target framework of the current project to add --> targetframework="net452" attribute in the package.config file
var currentTargetFw = Assembly.GetExecutingAssembly()
.GetCustomAttributes(typeof(TargetFrameworkAttribute), false);
var targetFrameworkAttribute = ((TargetFrameworkAttribute[]) currentTargetFw).FirstOrDefault();
// Update the packages.config file
packageReferenceFile.AddEntry(sourcePackage.GetFullName(),
SemanticVersion.Parse(sourcePackage.Version.ToFullString()), false,
new FrameworkName(targetFrameworkAttribute.FrameworkName));
}
}
This is working fine as a console app and is automatically reading the file correctly and updating the necessary references.
When i try to run this as an MsBuild task I keep running into errors.
An error has occurred during compilation. c:\Users\user\AppData\Local\Temp\dkkg20ya.0.cs(22,11) : error CS0246: The type or namespace name 'NuGet' could not be found (are you missing a using directive or an assembly reference?)
The task factory "CodeTaskFactory" could not be loaded from the assembly "C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\MSBuild\15.0\Bin\Microsoft.Build.Tasks.v15.0.dll". The task factory must return a value for the "TaskType" property.
This is the code I have put in the csproj (also moved to the nuget.targets to test)
<Target Name="BeforeBeforeBuild" BeforeTargets="BeforeBuild">
<UpdateNugetFiles />
</Target>
<UsingTask TaskName="UpdateNugetFiles" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v$(MSBuildToolsVersion).dll" >
<Task>
<Reference Include="System.Core" />
<Using Namespace="System" />
<Using Namespace="System.Linq" />
<Using Namespace="System.Reflection" />
<Using Namespace="System.Runtime.Versioning" />
<Using Namespace="NuGet" />
<Code Type="Fragment" Language="cs">
<![CDATA[
try {
// ---- Download and install a package at a desired path ----
var sourceUri = new Uri("FEED URL");
// ---- Update the ‘packages.config’ file ----
var packageReferenceFile = new PackageReferenceFile("../../packages.config");
string packagesPath = "../../packages";
IPackageRepository sourceRepository = PackageRepositoryFactory.Default.CreateRepository(sourceUri.ToString());
PackageManager packageManager = new PackageManager(sourceRepository, packagesPath);
foreach (var sourcePackage in sourceRepository.GetPackages().Where(x => x.IsLatestVersion))
{
if (!packageReferenceFile.EntryExists(sourcePackage.Id + " " + sourcePackage.Version, sourcePackage.Version))
{
var oldPackage = packageReferenceFile.GetPackageReferences().FirstOrDefault(x => x.Id.Contains(sourcePackage.Id));
if (oldPackage != null)
{
packageReferenceFile.DeleteEntry(oldPackage.Id, oldPackage.Version);
}
packageManager.InstallPackage(sourcePackage.Id, SemanticVersion.Parse(sourcePackage.Version.ToFullString()));
// Get the target framework of the current project to add targetframework="net452" attribute in the package.config file
currentTargetFw = Assembly.GetExecutingAssembly()
.GetCustomAttributes(typeof(TargetFrameworkAttribute), false);
var targetFrameworkAttribute = ((TargetFrameworkAttribute[]) currentTargetFw).FirstOrDefault();
// Update the packages.config file
packageReferenceFile.AddEntry(sourcePackage.GetFullName(),
SemanticVersion.Parse(sourcePackage.Version.ToFullString()), false,
new FrameworkName(targetFrameworkAttribute.FrameworkName));
}
}
return true;
}
catch (Exception ex) {
Log.LogErrorFromException(ex);
return false;
}
]]>
</Code>
</Task>
</UsingTask>
Any ideas on how to resolve this as cannot seem to find a solution.
Overall what to run this a pre step on a CI build to keep nugets up to date.
Thanks
Tim
Just call
nuget restore "your_solution.sln"
Don't reinvent the wheel by writing it in C# code.
Nuget Update Packages.config in MSBuild BeforeBuild Step
Not sure where your code issue comes from. It may be simpler just use NuGet.exe to restore and update the solution instead of trying to use C# code.
So you could add following nuget command line in the MSBuild BeforeBuild Step
<Target Name="BeforeBeforeBuild" BeforeTargets="BeforeBuild">
<Exec Command="$(YourNuGetPath)\nuget.exe restore "$(YouSolutionPath)\YourSolution.sln" -PackagesDirectory "$(YouPackagePath)\packages"" />
<Exec Command="$(YourNuGetPath)\nuget.exe update "$(YouSolutionPath)\YourSolution.sln"" />
</Target>
Note: If you are using Visual Studio, Visual Studio will automatically check the missing packages during the build and restore them: Package Restore.
Hope this helps.
after Microsoft marked the BuildEngine.Engine and BuildEngine.Project as obsolete i have tried to use the new proposal from Microsoft how you can see it underneath. But i have no idea where i can integrate the xmlprojectfile. Is here someone who knows the solution of this problem?
The XML project file content
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<ProjectToBuild Include ="myproject.csproj" />
</ItemGroup>
<Target Name="Build">
<MSBuild Projects="#(ProjectToBuild)"
Properties="Configuration=Debug" StopOnFirstFailure="true" />
</Target>
</Project>
The old and working version (but obsolete)
Microsoft.Build.BuildEngine.Engine engine = new Microsoft.Build.BuildEngine.Engine();
engine.DefaultToolsVersion = "4.0";
engine.RegisterLogger(new ConsoleLogger());
Microsoft.Build.BuildEngine.Project project = new Microsoft.Build.BuildEngine.Project(engine);
project.Load(xmlprojectfile);
if (!project.Build())
{
Console.ReadLine();
}
The new not working verison
Microsoft.Build.Evaluation.ProjectCollection collection = new Microsoft.Build.Evaluation.ProjectCollection();
collection.DefaultToolsVersion = "4.0";
collection.RegisterLogger(new ConsoleLogger());
Microsoft.Build.Evaluation.Project project = new Microsoft.Build.Evaluation.Project(collection);
if (!project.Build())
{
Console.ReadLine();
}
You also need to create a Microsoft.Build.Execution.BuildRequestData object containing the XML project file and a dictionary of std properties like Configuration and Platform. Then you create a Microsoft.Build.Execution.BuildParameters object containing your ProjectCollection object from your code snippet and pass that off to the default Microsoft.Build.Execution.BuildManager.
powershell pseudo code:
#set build properties
$props = new-object "System.Collections.Generic.Dictionary``2[[System.String],[System.String]]"
$props.Add("Configuration",$config)
$props.Add("Platform",$platform)
#create the projectCollection
$projectCollection = new-object Microsoft.Build.Evaluation.ProjectCollection -argumentList $props,$loggers,"ConfigurationFile,Registry"
$request = new-object Microsoft.Build.Execution.BuildRequestData -argumentlist $project,$props,$null,$targets,$null
#create a BuildParameters object to hold the Project Collection
$parameters = new-object Microsoft.Build.Execution.BuildParameters -argumentlist #($projectCollection)
$parameters.MaxNodeCount = 1
$parameters.Loggers = $projectCollection.Loggers
$parameters.ToolsetDefinitionLocations = "ConfigurationFile,Registry"
$parameters.DefaultToolsVersion = $toolsVersion
#get the build manager and submit a build request with the appropriate parameters
$manager = [Microsoft.Build.Execution.BuildManager]::DefaultBuildManager
$result = $manager.Build($parameters, $request)
You are missing project.Load(xmlprojectfile); line in your migrated code. You have to add xmlprojectfile into projectcollection somehow, I guess.
Instead of creating a new Project with new keyword:
// var project = new Project(collection);
Use this:
var project = collection.LoadProject("myproject.csproj")
I have small test app which has 2 resource files (Resources.resx & Resources.de-DE.resx) with the same exact string names, but one has the strings converted to German.
For my form I set the Localize property to ture.
In my application I am getting the strings as such:
this.Text = Properties.Resources.frmCaption;
In my release folder I get a de-DE folder with a dll named International_test.resources.dll.
I try to distribute this to a machine which is set to German and all of the strings pulled are still english.
I tried keeping the International_test.resources.dll in the de-DE folder or just put in in my apps directory.
What am I doing wrong or what do I need to do to get the German resource file to be used?
As luck would have it, I use hello world prototype project to test a whole bunch of stuff in our build pipeline.
Assuming you have setup your resource files correctly, here's some example code that may help. Code documentation removed for brevity.
public class HelloWorld
{
public CultureInfo CultureInfo { get; private set; }
public HelloWorld()
{
CultureInfo = CultureInfo.CurrentCulture;
}
public HelloWorld(string culture)
{
CultureInfo = CultureInfo.GetCultureInfo(culture);
}
public string SayHelloWorld()
{
return Resources.ResourceManager.GetString("HelloWorld", CultureInfo);
}
}
[TestFixture]
public class HelloWorldFixture
{
HelloWorld helloWorld;
[Test]
public void Ctor_SetsCultureInfo_ToCurrentCultureForParameterlessCtor()
{
helloWorld = new HelloWorld();
Assert.AreEqual(helloWorld.CultureInfo, CultureInfo.CurrentCulture,
"Expected CultureInfo to be set as CurrentCulture");
}
[Test]
public void Ctor_SetsCultureInfo_ToAustralianCulture()
{
helloWorld = new HelloWorld("en-AU");
Assert.AreEqual(helloWorld.CultureInfo.Name, "en-AU",
"Expected CultureInfo to be set to Australian culture");
}
[Test]
[ExpectedException(typeof(ArgumentException))]
public void Ctor_ThrowsException_InvalidCultureName()
{
helloWorld = new HelloWorld("Bogus");
}
[Test]
public void SayHelloWorld_ReturnsFallbackResource_OnUndefinedResource()
{
helloWorld = new HelloWorld("en-JM");
string result = helloWorld.SayHelloWorld();
Assert.AreEqual("Hello, World.", result, "Expected fallback resource string to be used");
}
[Test]
public void SayHelloWorld_ReturnsAustralianResource_OnAustralianResource()
{
helloWorld = new HelloWorld("en-AU");
string result = helloWorld.SayHelloWorld();
Assert.AreEqual("G'Day, World.", result, "Expected australian resource string to be used");
}
}
This project has Resources.resx file with HelloWorld string key item and "Hello, World" value, along with corresponding Resources.en-AU.resx with HelloWorld string key item and "G'Day, World" value, plus others such as zh-CH (我隻氣墊船裝滿晒鱔.:) to test display of non-English characters, as it gets displayed in the associated hello world web project.
Finally, Add some logging to show the culture being used (I took it out of this example for brevity), and also check your compiler output to ensure AL.exe is being invoked to link your resource assemblies (sounds like it's OK though).
I think my problem is that I am using a US version of XP with its language set to German as well other culture settings. The CurrentCulture and CurrentUICulture still appear to be still be "en-US"
I guess the best way to test then is to manually set the cultures at the start of the program like:
Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("de-DE");
Thread.CurrentThread.CurrentUICulture = CultureInfo.CreateSpecificCulture("de-DE");
thanks a ton Si.
I finally got my problem figured out.
My Resource files are in the Properties folder so I need to put the following
//using embedded resources
ResourceManager rm = new ResourceManager("International_test.Properties.Resources", Assembly.GetExecutingAssembly());