Flow.Launcher icon indicating copy to clipboard operation
Flow.Launcher copied to clipboard

DEMO: Localization source generators and analyzers

Open Yusyuriv opened this issue 1 year ago • 7 comments

[!WARNING] This is just a demonstration to gather feedback. This isn't necessarily going to make it into the codebase. This PR shouldn't be merged, at least in the current state.

The problem

Currently, localization is located in .xaml files and used in code like this:

InternationalizationManager.Instance.GetTranslation("checkUpdates");

Or like this, if the string has variables inside:

string.Format(
    InternationalizationManager.Instance.GetTranslation("update_flowlauncher_fail_moving_portable_user_profile_data"),
    locationFrom,
    locationTo
);

This has a few issues:

  1. It is error-prone. It's very easy to mistype a key in the string and not notice. Even if you don't mistype it, you could accidentally change it later and not notice.
  2. No autocomplete. There may be a string present in the XAML file already, and you roughly know its name, but you can't just start typing it for your IDE to suggest one of the existing strings. You will need to go to en.xaml, look it up there, and copy the key from there. It's very inconvenient.

Proposed solution

This PR implements a source generator that takes en.xaml file, parses it and generates a static class with methods. Each method's name is a key from en.xaml, and the return value is the translated string. Optionally, the method receives arguments if the translated value has placeholders ({0}). It also generates doc comments so you could hover a method and see what the actual string looks like. Here's an example of generated code:

namespace Current.Project.Namespace;

public static class Localize
{
/// <summary>
/// <code>
/// Check for Updates
/// </code>
/// </summary>
public static string checkUpdates() => InternationalizationManager.Instance.GetTranslation("checkUpdates");

/// <summary>
/// <code>
/// Become A Sponsor
/// </code>
/// </summary>
public static string BecomeASponsor() => InternationalizationManager.Instance.GetTranslation("BecomeASponsor");

/// <summary>
/// <code>
/// New version {version} is available, would you like to restart Flow Launcher to use the update?
/// </code>
/// </summary>
public static string newVersionTips(string version) => string.Format(InternationalizationManager.Instance.GetTranslation("newVersionTips"), version);
}

This is used like this:

var version = Localize.newVersionTips("1.0.0");

The Analyzers project includes several diagnostics and automatic fixes for them needed for the source generator to work. For example, when used in plugin projects, Localize class expects the main class of your plugin (the one implementing IPluginI18n) to have an internal or a public static property of type PluginInitContext. It will use that property in the generated code like this:

public static string BecomeASponsor() => Main.Context.GetTranslation("BecomeASponsor");

The analyzers will look at the main class of the plugin and show errors when it's impossible to generate the Localize class. A few examples:

This property is private and isn't static, so it's unavailable from the Localize class: image

This property is static, but it's still private, so it's unavailable from the Localize class: image

This is a private field, so it's unavailable from the Localize class: image

There's also an analyzer that warns about using the old localization method: image

All of these offer an automatic code fix (Alt+Enter in Rider).

To add the source generator and the analyzers to a project, this code should be added to .csproj file:

  <ItemGroup>
    <ProjectReference
      Include="..\..\Flow.Launcher.Analyzers\Flow.Launcher.Analyzers.csproj"
      ReferenceOutputAssembly="false"
      OutputItemType="Analyzer" />
    <ProjectReference
      Include="..\..\Flow.Launcher.SourceGenerators\Flow.Launcher.SourceGenerators.csproj"
      ReferenceOutputAssembly="false"
      OutputItemType="Analyzer" />
    <AdditionalFiles Include="Languages\en.xaml" />
  </ItemGroup>

<AdditionalFiles /> is needed because source generators don't have access to files unless they're specified in <AdditionalFiles />.

~~Unfortunately, I haven't managed to make analyzers work in Visual Studio, but they work well in Rider and VS Code.~~ Apparently, Visual Studio allows using only .NET Standard 2.0 for analyzers. Modified the code for .NET Standard 2.0, it now works in Visual Studio.

What's included in this PR

  1. The source generator
  2. The analyzers
  3. Flow.Launcher.Core project uses the source-generated Localize class everywhere it was using InternationalizationManager.Instance.GetTranslation()
  4. plugins/Flow.Launcher.Plugin.BrowserBookmark and plugins/Flow.Launcher.Plugin.Calculator have the source generator and the analyzers included, but no other changes introduced, so you could see for yourself how it works if you're interested to try. They're intentionally left in a broken state to show that the analyzer prevents them from building until the errors preventing the source generator from generating the Localize class are fixed.

What now

I don't know, I guess I just wanted to hear what you think about all this. It has been a great learning exercise for me either way.

Yusyuriv avatar May 25 '24 23:05 Yusyuriv

I think if we are going to merge this pr, we will want to release this to nuget. @jjw24 @JohnTheGr8 do you know how are we going to do that similar to the plugin package?

taooceros avatar Jun 01 '24 23:06 taooceros

I could merge the SourceGenerators and Analyzers projects into one if it makes things easier.

Yusyuriv avatar Jun 02 '24 00:06 Yusyuriv

@Yusyuriv cool idea, this could be very helpful...

I think if we are going to merge this pr, we will want to release this to nuget. @jjw24 @JohnTheGr8 do you know how are we going to do that similar to the plugin package?

Does it not make more sense to move these two projects to a separate repository?

We don't want to complicate this repository's CI work any further, and the release of the generators/analyzers should be independent of the launcher itself... (unlike the plugin project)

@jjw24 thoughts?

JohnTheGr8 avatar Jun 06 '24 07:06 JohnTheGr8

I also thought about making the class name something very short for convenience, like this, but I'm not sure if it's worth it over the readability of Localize:

- Localize.newVersionTips("1.0.0")
+ L.newVersionTips("1.0.0")

Yusyuriv avatar Jun 06 '24 15:06 Yusyuriv

I also thought about making the class name something very short for convenience, like this, but I'm not sure if it's worth it over the readability of Localize:


- Localize.newVersionTips("1.0.0")

+ L.newVersionTips("1.0.0")

I wouldn't go with that. With modern ide Localize only requires 2-3 strokes which is not significant. And generally people don't need to use it a lot.

taooceros avatar Jun 06 '24 15:06 taooceros

Good idea, and all comments are valid. I confirm we should have the generators/analyzers projects independent of the launcher itself.

jjw24 avatar Jul 25 '24 11:07 jjw24

Walkthrough

Walkthrough

The changes introduce a set of diagnostic analyzers and code fix providers within the Flow Launcher framework, focusing on localization best practices. New project files for analyzers and source generators are created, along with various diagnostic descriptors that help identify issues related to localization in plugin development. Additionally, updates are made to existing project files to integrate these analyzers and improve localization handling throughout the application.

Changes

File Change Summary
Flow.Launcher.Analyzers/AnalyzerDiagnostics.cs Added a static class with five diagnostic descriptors for localization-related issues in plugins.
Flow.Launcher.Analyzers/AnalyzerReleases.Shipped.md New documentation file for shipped analyzer releases.
Flow.Launcher.Analyzers/AnalyzerReleases.Unshipped.md Introduced unshipped analyzer rules for localization with five new rules and severity levels.
Flow.Launcher.Analyzers/Flow.Launcher.Analyzers.csproj Created a project file targeting .NET Standard 2.0 with necessary package references.
Flow.Launcher.Analyzers/Localize/ContextAvailabilityAnalyzer.cs Added a diagnostic analyzer for checking context property availability in plugins.
Flow.Launcher.Analyzers/Localize/ContextAvailabilityAnalyzerCodeFixProvider.cs Implemented a code fix provider for context property-related diagnostics.
Flow.Launcher.Analyzers/Localize/OldGetTranslateAnalyzer.cs Defined a diagnostic analyzer for deprecated localization API usage.
Flow.Launcher.Analyzers/Localize/OldGetTranslateAnalyzerCodeFixProvider.cs Created a code fix provider for handling deprecated localization API diagnostics.
Flow.Launcher.Core/Flow.Launcher.Core.csproj Added project references for analyzers and source generators, along with an additional files entry.
Flow.Launcher.Core/Resource/Theme.cs Updated error messages to use localized methods instead of string formatting.
Flow.Launcher.Core/Updater.cs Replaced old localization API calls with new Localize class calls in update process messages.
Flow.Launcher.SourceGenerators/AnalyzerReleases.Shipped.md New documentation file for shipped analyzer releases in source generators.
Flow.Launcher.SourceGenerators/AnalyzerReleases.Unshipped.md Introduced unshipped analyzer rules for localization with seven new rules and severity levels.
Flow.Launcher.SourceGenerators/Flow.Launcher.SourceGenerators.csproj Created a project file targeting .NET Standard 2.0 with necessary package references.
Flow.Launcher.SourceGenerators/Localize/LocalizeSourceGenerator.cs Implemented a source generator for localization, processing XAML files and generating code for localized strings.
Flow.Launcher.SourceGenerators/SourceGeneratorDiagnostics.cs Defined a static class with diagnostic descriptors for localization issues in plugins.
Flow.Launcher.sln Added new projects for analyzers and source generators to the solution file.
Flow.Launcher/Languages/en.xaml Modified string resources by adding commented-out parameter declarations for future use.
Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj Added project references for analyzers and source generators, along with an additional files entry.
Plugins/Flow.Launcher.Plugin.Calculator/Flow.Launcher.Plugin.Calculator.csproj Added project references for analyzers and source generators, along with an additional files entry.

Possibly related PRs

  • #2769: This PR is related as it involves localization changes, specifically the extraction of hard-coded messages into the en.xaml file, similar to the diagnostic descriptors defined in the main PR for localization best practices.
  • #2918: This PR also involves modifications to the en.xaml file, adding new localization strings for the Explorer plugin, which aligns with the main PR's focus on enhancing localization practices in the Flow Launcher framework.

Suggested labels

Explorer Plugin, 1 min review

Poem

🐰 In the land of code where plugins play,
New diagnostics bloom, brightening the way.
With fixes and helpers, localization's the key,
Flow Launcher hops forward, as happy as can be!
So let’s cheer for the changes, both big and small,
A leap for the plugins, let’s celebrate them all! 🌟


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

Share
Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example: -- I pushed a fix in commit <commit_id>, please review it. -- Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples: -- @coderabbitai generate unit testing code for this file. -- @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples: -- @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase. -- @coderabbitai read src/utils.ts and generate unit testing code. -- @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format. -- @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

coderabbitai[bot] avatar Sep 24 '24 02:09 coderabbitai[bot]

@check-spelling-bot Report

:red_circle: Please review

See the :open_file_folder: files view, the :scroll:action log, or :memo: job summary for details.

:x: Errors Count
:x: forbidden-pattern 22
:warning: ignored-expect-variant 1
:warning: non-alpha-in-dictionary 19

See :x: Event descriptions for more information.

If the flagged items are :exploding_head: false positives

If items relate to a ...

  • binary file (or some other file you wouldn't want to check at all).

    Please add a file path to the excludes.txt file matching the containing file.

    File paths are Perl 5 Regular Expressions - you can test yours before committing to verify it will match your files.

    ^ refers to the file's path from the root of the repository, so ^README\.md$ would exclude README.md (on whichever branch you're using).

  • well-formed pattern.

    If you can write a pattern that would match it, try adding it to the patterns.txt file.

    Patterns are Perl 5 Regular Expressions - you can test yours before committing to verify it will match your lines.

    Note that patterns can't match multiline strings.

github-actions[bot] avatar Mar 03 '25 12:03 github-actions[bot]

@check-spelling-bot Report

:red_circle: Please review

See the :open_file_folder: files view, the :scroll:action log, or :memo: job summary for details.

:x: Errors Count
:x: forbidden-pattern 22
:warning: ignored-expect-variant 1
:warning: non-alpha-in-dictionary 19

See :x: Event descriptions for more information.

If the flagged items are :exploding_head: false positives

If items relate to a ...

  • binary file (or some other file you wouldn't want to check at all).

    Please add a file path to the excludes.txt file matching the containing file.

    File paths are Perl 5 Regular Expressions - you can test yours before committing to verify it will match your files.

    ^ refers to the file's path from the root of the repository, so ^README\.md$ would exclude README.md (on whichever branch you're using).

  • well-formed pattern.

    If you can write a pattern that would match it, try adding it to the patterns.txt file.

    Patterns are Perl 5 Regular Expressions - you can test yours before committing to verify it will match your lines.

    Note that patterns can't match multiline strings.

github-actions[bot] avatar Mar 03 '25 13:03 github-actions[bot]

@check-spelling-bot Report

:red_circle: Please review

See the :open_file_folder: files view, the :scroll:action log, or :memo: job summary for details.

:x: Errors Count
:x: forbidden-pattern 22
:warning: ignored-expect-variant 1
:warning: non-alpha-in-dictionary 19

See :x: Event descriptions for more information.

If the flagged items are :exploding_head: false positives

If items relate to a ...

  • binary file (or some other file you wouldn't want to check at all).

    Please add a file path to the excludes.txt file matching the containing file.

    File paths are Perl 5 Regular Expressions - you can test yours before committing to verify it will match your files.

    ^ refers to the file's path from the root of the repository, so ^README\.md$ would exclude README.md (on whichever branch you're using).

  • well-formed pattern.

    If you can write a pattern that would match it, try adding it to the patterns.txt file.

    Patterns are Perl 5 Regular Expressions - you can test yours before committing to verify it will match your lines.

    Note that patterns can't match multiline strings.

github-actions[bot] avatar Mar 03 '25 13:03 github-actions[bot]

Separated project on https://github.com/Flow-Launcher/Flow.Launcher.Localization.

Jack251970 avatar Mar 04 '25 03:03 Jack251970