Update to .NETStandard 2.0 and Localization - xamarin

I followed these instructions to upgrade my Xamarin.Forms project to .NETStandard 2.0. Now I added a new entry to my .resx file and get a compile error:
error CS0117: 'AppResources' does not contain a definition for 'MyKey'
I think the AppResources.Designer.cs file is not updated anymore. The following things seems to be wrong:
According to the instructions I should delete AssemblyInfo.cs, which I have done only for the PCL and not the indivdual projects (iOS, Droid). On the other side you should add NeutralResourcesLanguage to the AssemblyInfo.cs according to the docs. So should I have a AssemblyInfo.cs or not?
I cannot change the access modifier if I open AppResources.resx (works after manipulating .csproj)
I only have this code in my new .csproj of the PCL
<ItemGroup>
<EmbeddedResource Update="Common\Localization\AppResources.resx">
<Generator>ResXFileCodeGenerator </Generator>
</EmbeddedResource>
</ItemGroup>
In the detailed build logs I found this and I don't know what I should do with this
1> Task "CreateCSharpManifestResourceName" skipped, due to false
condition; ('%(EmbeddedResource.ManifestResourceName)' == '' and
'%(EmbeddedResource.WithCulture)' == 'true' and
'%(EmbeddedResource.Type)' == 'Non-Resx') was evaluated as ('' == ''
and 'false' == 'true' and 'Resx' == 'Non-Resx'). 1> Task
"CreateCSharpManifestResourceName" skipped, due to false condition;
('%(EmbeddedResource.ManifestResourceName)' == '' and
'%(EmbeddedResource.WithCulture)' == 'true' and
'%(EmbeddedResource.Type)' == 'Non-Resx') was evaluated as ('' == ''
and 'true' == 'true' and 'Resx' == 'Non-Resx'). 1> Task
"CreateCSharpManifestResourceName" skipped, due to false condition;
('%(EmbeddedResource.ManifestResourceName)' == '' and
'%(EmbeddedResource.WithCulture)' == 'true' and
'%(EmbeddedResource.Type)' == 'Non-Resx') was evaluated as ('' == ''
and 'false' == 'true' and 'Non-Resx' == 'Non-Resx').
My best bet would be to manually edit the .csproj from the PCL project. In my old .csproj I had this:
<Compile Include="Common\Localization\AppResources.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>AppResources.resx</DependentUpon>
</Compile>
<ItemGroup>
<EmbeddedResource Include="Common\Localization\AppResources.de.resx">
<SubType>Designer</SubType>
</EmbeddedResource>
<EmbeddedResource Include="Common\Localization\AppResources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>AppResources.Designer.cs</LastGenOutput>
<SubType>Designer</SubType>
</EmbeddedResource>
</ItemGroup>
But I don't think I can adopt it one-to-one. I tried to play with it, but didn't had success:
<ItemGroup>
<Compile Update="Common\Localization\AppResources.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>AppResources.resx</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Common\Localization\AppResources.de.resx">
<SubType>Designer</SubType>
</EmbeddedResource>
<EmbeddedResource Update="Common\Localization\AppResources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>AppResources.Designer.cs</LastGenOutput>
<SubType>Designer</SubType>
</EmbeddedResource>
</ItemGroup>
How do I get my translation working again?

Seems the only thing that was needed is to set the Custom Tool option of AppResources.resx to PublicResXFileCodeGenerator. This results into the following .csproj code:
<ItemGroup>
<Compile Update="Common\Localization\AppResources.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>AppResources.resx</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Common\Localization\AppResources.de.resx">
<SubType>Designer</SubType>
</EmbeddedResource>
<EmbeddedResource Update="Common\Localization\AppResources.resx">
<Generator>PublicResXFileCodeGenerator</Generator>
<LastGenOutput>AppResources.Designer.cs</LastGenOutput>
<SubType>Designer</SubType>
</EmbeddedResource>
</ItemGroup>
The Access Modifier for the individual key/value pairs of AppResources.resx now is Public. All other language resource files have the Acces Modifier of No code generation.
I also deleted the other AssemblyInfo.cs files as it seems that it works without them and without NeutralResourcesLanguage as opposed to the sample in the docs. Nevertheless, I set the Assembly netural language under Package in the project properties. Therefore the .csproj now contains an <NeutralLanguage>en</NeutralLanguage> entry.
What I find odd is that you can't add a Resource file in Visual Studio anymore ...

Related

I want to setting T4 Template with wildcard in csproj

What I do
I have multiple tt files in my project file.
Therefore, many entries are created for each file as follows.
<ItemGroup>
<Compile Update="Sample.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Sample.tt</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<None Update="Sample.tt">
<Generator>TextTemplatingFileGenerator</Generator>
<LastGenOutput>Sample.cs</LastGenOutput>
</None>
</ItemGroup>
I want to prevent making this entry for every tt file.
What I have tried
The following statements were made using wildcards.
<ItemGroup>
<Compile Update="**/*.tt.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>%(Filename)</DependentUpon>
</Compile>
<None Update="**/*.tt">
<Generator>TextTemplatingFileGenerator</Generator>
<LastGenOutput>%(Filename).tt.cs</LastGenOutput>
</None>
</ItemGroup>
The following settings are used in the tt file.
<## output extension="tt.cs" #>
Problem
T4 works, but when I save the tt file, the following entry is added to csproj.
<ItemGroup>
<Compile Update="Sample.tt.cs">
<DesignTime>True</DesignTime>
</Compile>
</ItemGroup>
Question
How can I prevent entries from being added?
We can see the description of Update from this page
Enables you to modify metadata of an item; typically used to override
the default metadata of specific items after a group of items is
intially specified (such as with a wildcard).
Update overrides Compile when save the tt file, so this code will be added to scproj. This doesn't seem to prevent.
<ItemGroup>
<Compile Update="Sample.tt.cs">
<DesignTime>True</DesignTime>
</Compile>
</ItemGroup>

How do I prevent text template generator from running on every VS build?

I have a VS 2019 solution with several projects. There is one project that every other project depends on and I have some T4 templates in that project. The templates are regenerated every time I invoke the Build command (no changes) and therefore all the dependent projects are also rebuilt.
How can I fix this so that the templates are only regenerated when necessary? My project file has the following:
<PropertyGroup>
<TransformOnBuild>true</TransformOnBuild>
<OverwriteReadOnlyOutputFiles>true</OverwriteReadOnlyOutputFiles>
<TransformOutOfDateOnly>true</TransformOutOfDateOnly>
</PropertyGroup>
<Import Project="$(VSToolsPath)\TextTemplating\Microsoft.TextTemplating.targets" />
<ItemGroup>
<None Update="Messages\Messages.tt">
<Generator>TextTemplatingFileGenerator</Generator>
<LastGenOutput>Messages.generated.cs</LastGenOutput>
</None>
<EmbeddedResource Update="Messages\Messages.de.resx" />
<EmbeddedResource Update="Messages\Messages.resx">
<Generator>PublicResXFileCodeGenerator</Generator>
<LastGenOutput>Messages.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
<Target Name="CallTransformOnBuild" BeforeTargets="CoreCompile">
<CallTarget Targets="TransformDuringBuild" />
</Target>
I only want the transform to run on build if:
Messages.Generated.cs doesn't exist
Messages.tt changed
Messages.resx changed
You need to make sure the following XML Tags' are set to false.
<TransformOnBuild>False</TransformOnBuild>
<OverwriteReadOnlyOutputFiles>False</OverwriteReadOnlyOutputFiles>

How can I recognize an ASP.NET Core project in a Visual Studio extension

In my Visual Studio Package, I look at each project in the solution and I need to determine what kind of project it is.
Historically I use technique something like the one described by Carlos Quintero here to get the project GUIDs and look for specific GUIDs in the list.
For ASP.NET Core projects, I have looked for
AspNetCore: "{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}"
AspNetCore2: "{9A19103F-16F7-4668-BE54-9A1E7A4F7556}"
This will probably still work, if the project uses an old-style project file.
However, it does not work if the new Visual Studio Project system is used.
This is the project file that I am currently using:
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0" />
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="2.0.0" />
</ItemGroup>
<ItemGroup>
<DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="2.0.0" />
</ItemGroup>
<ItemGroup>
<Compile Update="App_GlobalResources\Resources.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="App_GlobalResources\Resources.resx">
<Generator>PublicResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
<EmbeddedResource Update="Resources\Controllers\App_LocalResources\HomeController.resx.en.resx">
<Generator></Generator>
</EmbeddedResource>
<EmbeddedResource Update="Resources\Controllers\HomeController.en.resx">
<Generator></Generator>
</EmbeddedResource>
<EmbeddedResource Update="Resources\Controllers\HomeController.nl.resx">
<Generator></Generator>
</EmbeddedResource>
<EmbeddedResource Update="Resources\Controllers\HomeController.resx">
<Generator></Generator>
</EmbeddedResource>
<EmbeddedResource Update="Resources\Controllers\StudentController.en.resx">
<Generator></Generator>
</EmbeddedResource>
<EmbeddedResource Update="Resources\Models\Student.en.resx">
<Generator></Generator>
</EmbeddedResource>
<EmbeddedResource Update="Resources\Views\Home\About.resx">
<Generator></Generator>
</EmbeddedResource>
<EmbeddedResource Update="Resources\Views\Home\Contact.en.resx">
<Generator></Generator>
</EmbeddedResource>
<EmbeddedResource Update="Resources\Views\Home\Index.en.resx">
<Generator></Generator>
</EmbeddedResource>
<EmbeddedResource Update="Resources\Views\Home\Index.resx">
<Generator></Generator>
</EmbeddedResource>
</ItemGroup>
</Project>
As you can see, it does not contain the project GUIDs.
Using the technique described in How to detect whether a project is a CPS project, I can detect that it is a CPS (Common Project System) project.
Using the technique described in Finding CPS in a VS project, I am able to get hold of the "unconfigured project", and I could also get the "configured project" (although I'm not sure I understand what "unconfigured" and "configured" mean in this context).
At this point I am stuck. I can't figure out how to determine if it is an ASP.NET Core project.
The best bet is probably to get the TargetFramework attribute, which is netcoreapp2.0. Is there any way that to get the TargetFramework attribute from the CPS project object?
Or am I going about this in entirely the wrong way?
You can look for the TargetFrameworkMoniker:
public static bool IsNetCore2(this Project project)
{
return project.Properties.Item("TargetFrameworkMoniker").Value.ToString().Contains(".NETCoreApp,Version=v2.");
}
https://github.com/ErikEJ/EFCorePowerTools/blob/master/src/GUI/EFCorePowerTools/Extensions/ProjectExtensions.cs#L75
To detect ASP.NET Core, I think you will have to parse the csproj file:
and look for Sdk="Microsoft.NET.Sdk.Web"
Probably a good solution is to use the MsBuild API, in the nuget package Microsoft.Build.
If you have the ENVDte.Project object, you can get the path to the project file using the FullName property.
Then you can access the Sdk property as follows:
void foo ( ENVDte.Project prj )
{
var MsBuildProject = new Microsoft.Build.Evaluation.Project ( prj.FullName ) ;
if ( string.IsNullOrEmpty ( MsBuildProject.Xml.Sdk ) )
{
// Not .NET core
}
else if ( MsBuildProject.Xml.Sdk == "Microsoft.NET.Sdk.Web" )
{
// ASP.NET core
}
else if ( MsBuildProject.Xml.Sdk == "Microsoft.NET.Sdk.WindowsDesktop" )
{
// .NET core desktop app
}
}
This is really just the same as parsing the project file and looking for Sdk="Microsoft.NET.Sdk.Web", as Eric already suggested, but I prefer to use an API if possible.
If you are looking for other details about the project, there is a lot more information in the MsBuild project object, for example in the collection AllEvaluatedItems or the collection AllEvaluatedProperties.

Include build directory in nuget package using visual studio pack

I'm attempting to create a nupkg with Visual Studio using the built in nuget package building and include the build directory from my project in the nupkg. It seems like it should be a fairly simple task but I can't get it to work. From my googling adding either of these to my csproj file should work, but both create an empty 'build' directory in the nupkg:
<ItemGroup>
<None Include="build\**">
<Pack>true</Pack>
<PackagePath>build\</PackagePath>
<IncludeInPackage>true</IncludeInPackage>
</None>
</ItemGroup>
Using nuget pack to create the package with the following in my nuspec does work:
<files>
<!-- Include everything in \build -->
<file src="build\**" target="build" />
</files>
Include build directory in nuget package using visual studio pack
According to the document Including content in a package, you should use the properties <Pack>true</Pack> and <PackagePath>build\</PackagePath>:
If you want to copy all your content to only a specific root folder(s) (instead of content and contentFiles both), you can use the MSBuild property ContentTargetFolders, which defaults to "content;contentFiles" but can be set to any other folder names.
PackagePath can be a semicolon-delimited set of target paths.
Specifying an empty package path would add the file to the root of the
package.
So, you can change your ItemGroup like following:
<ItemGroup>
<None Include="build\**" Pack="True" PackagePath="build\" />
</ItemGroup>
Update:
I believe this is the same as what I added but in a different XML
structure and without the Pack attribute
The Pack attribute is the key point. It works fine with your XML structure and the Pack attribute. You should make sure you have the files in the build folder in your project folder:
Check my test demo below:
Update2:
Ah! You are using the .net framework project!! That the reason for this issue. This method is used for .net standard and .net core project by default and it not work for .net framework. To resolve this issue you have to use the .nupsec file, like you post in the question.
If you still want to include build directory in nuget package using visual studio pack, you need change your project type to SDK type:
Check this document for some more details.
Then you can use the method, which we talked about before.
Hope this helps.
The solution to this issue was to upgrade the project to SDK type (Xamarin binding projects by default use the old format but seem to work with the new type) and then use:
<ItemGroup>
<None Update="build\**">
<IncludeInPackage>true</IncludeInPackage>
</None>
</ItemGroup>
To include the build directory. The alternative is using nuget pack.
When converting the project make sure to leave in the Xamarin import:
<Import Project="$(MSBuildExtensionsPath)\Xamarin\iOS\Xamarin.iOS.ObjCBinding.CSharp.targets" />
Here's how my project file looks afterwards:
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk" ToolsVersion="15.0">
<PropertyGroup>
<PackageId></PackageId>
<PackageVersion>3.3.2</PackageVersion>
<ReleaseVersion>$(PackageVersion)</ReleaseVersion>
<AssemblyVersion>$(PackageVersion)</AssemblyVersion>
<Authors>Nick Brook</Authors>
<Description></Description>
<Copyright></Copyright>
<PackageProjectUrl></PackageProjectUrl>
<Summary></Summary>
<PackageTags></PackageTags>
<Title></Title>
<PackageReleaseNotes>Initial Release</PackageReleaseNotes>
<OutputType>Library</OutputType>
<IPhoneResourcePrefix>Resources</IPhoneResourcePrefix>
<OutputPath>bin\$(Configuration)</OutputPath>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<Optimize>true</Optimize>
<PackageOutputPath>packed</PackageOutputPath>
<PackOnBuild>true</PackOnBuild>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="Xamarin.iOS" />
</ItemGroup>
<ItemGroup>
<ObjcBindingApiDefinition Include="ApiDefinition.cs" />
</ItemGroup>
<ItemGroup>
<ObjcBindingCoreSource Include="Structs.cs" />
</ItemGroup>
<ItemGroup>
<Compile Remove="Structs.cs" Condition=" '$(EnableDefaultCompileItems)' == 'true' " />
<Compile Remove="ApiDefinition.cs" Condition=" '$(EnableDefaultCompileItems)' == 'true' " />
</ItemGroup>
<ItemGroup>
<None Remove="packed\**" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Xamarin.Build.Download" Version="0.4.11" />
<PackageReference Include="NuGet.Build.Packaging" Version="0.2.2" />
</ItemGroup>
<ItemGroup>
<None Update="build\**">
<IncludeInPackage>true</IncludeInPackage>
</None>
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Xamarin\iOS\Xamarin.iOS.ObjCBinding.CSharp.targets" />
</Project>

Localized resx files as sub items in solution explorer

We have a large number of C# projects in a visual studio solution. For a lot of these we have a resource file called 'TextResources.resx'. These have been translated in the past creating several related resx files for example 'TextResources.fr.resx'. For these older projects these translated resx files are showing as sub items of the default english TextResources.resx however for several new projects the translated items are appearing as separate (same tree level) items.
Hopefully this diagram will explain:
Old Projects:
- TextResources.resx
- TextResources.fr.resx
- TextResources.de.resx
New Projects:
- TextResources.fr.resx
- TextResources.de.resx
- TextResources.resx
This not only looks odd but is a little confusing with so many projects. Anyone know why its grouping some translated resx files but not others?
I had the same problem and found no answer either.
I ended up editing the project (.csproj) file by hand.
<EmbeddedResource Include="Folder\TextResources.de.resx">
<DependentUpon>TextResources.resx</DependentUpon>
<SubType>Designer</SubType>
</EmbeddedResource>
Add the
<DependentUpon></DependentUpon>
tag and type out the name of the "parent" resource file without leading folders.
Check out the custom file nesting feature in VS2019 here's an example: -
https://mitch.codes/tip-visual-studio-custom-file-nesting/
Once you have the [customised] "xx.filenesting.json" file open for editing, just add an entry for ".resx" in the path segments section: -
look in %USERPROFILE%\AppData\Local\Microsoft\VisualStudio\NestingProfiles
"pathSegment": {
"add": {
".*": [
".js",
".css",
".html",
".htm",
".less",
".scss",
".coffee",
".iced",
".config",
".cs",
".vb",
".json",
".resx" <-----
]
}
},
This solution for Visual Studio 2019
<!-- Main Resource file -->
<ItemGroup>
<EmbeddedResource Update="Path\TextResources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>TextResources.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
<!-- Resource Designer class -->
<ItemGroup>
<Compile Update="Path\TextResources.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>TextResources.resx</DependentUpon>
</Compile>
</ItemGroup>
<!-- Nested Resources files -->
<ItemGroup>
<EmbeddedResource Update="Path\TextResources.en.resx">
<DependentUpon>Path\TextResources.resx</DependentUpon>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Path\TextResources.de.resx">
<DependentUpon>Path\TextResources.resx</DependentUpon>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Path\TextResources.ar.resx">
<DependentUpon>Path\TextResources.resx</DependentUpon>
</EmbeddedResource>
</ItemGroup>

Resources