diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 0195fa6..aa0dbf7 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -3,6 +3,7 @@ name: CI
on:
push:
branches: [ "main" ]
+ workflow_dispatch:
jobs:
prepare:
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index e80aa27..e0fbcdc 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -3,31 +3,30 @@ name: Release
on:
push:
tags:
- - "v*.*.*"
+ - "v*.*.*.*"
+ workflow_dispatch:
jobs:
-
prepare:
-
runs-on: ubuntu-latest
steps:
- - name: Checkout
- uses: actions/checkout@v4
- with:
- fetch-depth: 0
-
- - name: Determine version
- id: version
- uses: paulhatch/semantic-version@v5.4.0
- with:
- version_format: "${major}.${minor}.${patch}"
-
- - name: Upload certificate
- uses: actions/upload-artifact@v4
- with:
- name: dist
- path: dist/code_signing.cer
+ - name: Checkout
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+
+ - name: Determine version
+ id: version
+ uses: paulhatch/semantic-version@v5.4.0
+ with:
+ version_format: "${major}.${minor}.${patch}"
+
+ - name: Upload certificate
+ uses: actions/upload-artifact@v4
+ with:
+ name: dist
+ path: dist/code_signing.cer
outputs:
version: ${{ steps.version.outputs.version}}
@@ -37,76 +36,78 @@ jobs:
needs: prepare
steps:
- - name: Checkout
- uses: actions/checkout@v4
- with:
- fetch-depth: 0
-
- - name: Build runtime dependent binaries
- uses: "./.github/template/build-signed"
- with:
- dotnet_args: "-p VersionPrefix=${{ needs.prepare.outputs.version }}"
- package_project: dist/Dependent/Dependent.wixproj
- package_version: ${{ needs.prepare.outputs.version }}
- package: dist\Dependent\bin\Release\BrowserPicker.msi
- package_name: DependentSetup-${{ needs.prepare.outputs.version }}-Release
- package_path: dist/Dependent/bin/Release
- binaries: |
- src\BrowserPicker.App\bin\Release\net9.0-windows\publish\BrowserPicker*.dll src\BrowserPicker.App\bin\Release\net9.0-windows\publish\BrowserPicker*.exe
- bundle_name: Dependent-${{ needs.prepare.outputs.version }}-Release
- bundle_path: src/BrowserPicker.App/bin/Release/net9.0-windows/publish
+ - name: Checkout
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+
+ - name: Build runtime dependent binaries
+ uses: "./.github/template/build-signed"
+ with:
+ dotnet_args: "-p VersionPrefix=${{ needs.prepare.outputs.version }}"
+ package_project: dist/Dependent/Dependent.wixproj
+ package_version: ${{ needs.prepare.outputs.version }}
+ package: dist\Dependent\bin\Release\BrowserPicker.msi
+ package_name: DependentSetup-${{ needs.prepare.outputs.version }}-Release
+ package_path: dist/Dependent/bin/Release
+ binaries: |
+ src\BrowserPicker.App\bin\Release\net9.0-windows\publish\BrowserPicker*.dll src\BrowserPicker.App\bin\Release\net9.0-windows\publish\BrowserPicker*.exe
+ bundle_name: Dependent-${{ needs.prepare.outputs.version }}-Release
+ bundle_path: src/BrowserPicker.App/bin/Release/net9.0-windows/publish
portable:
runs-on: windows-latest
needs: prepare
steps:
- - name: Checkout
- uses: actions/checkout@v4
- with:
- fetch-depth: 0
-
- - name: Build runtime independent binaries
- uses: "./.github/template/build-signed"
- with:
- dotnet_args: "-p VersionPrefix=${{ needs.prepare.outputs.version }} -r win-x64 -p:PublishSingleFile=true"
- package_project: dist/Portable/Portable.wixproj
- package_version: ${{ needs.prepare.outputs.version }}
- package: dist\Portable\bin\Release\BrowserPicker-Portable.msi
- package_name: PortableSetup-${{ needs.prepare.outputs.version }}-Release
- package_path: dist/Portable/bin/Release
- binaries: src\BrowserPicker.App\bin\Release\net9.0-windows\win-x64\publish\BrowserPicker.exe
- bundle_name: Portable-${{ needs.prepare.outputs.version }}-Release
- bundle_path: src/BrowserPicker.App/bin/Release/net9.0-windows/win-x64/publish
+ - name: Checkout
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+
+ - name: Build runtime independent binaries
+ uses: "./.github/template/build-signed"
+ with:
+ dotnet_args: "-p VersionPrefix=${{ needs.prepare.outputs.version }} -r win-x64 -p:PublishSingleFile=true"
+ package_project: dist/Portable/Portable.wixproj
+ package_version: ${{ needs.prepare.outputs.version }}
+ package: dist\Portable\bin\Release\BrowserPicker-Portable.msi
+ package_name: PortableSetup-${{ needs.prepare.outputs.version }}-Release
+ package_path: dist/Portable/bin/Release
+ binaries: src\BrowserPicker.App\bin\Release\net9.0-windows\win-x64\publish\BrowserPicker.exe
+ bundle_name: Portable-${{ needs.prepare.outputs.version }}-Release
+ bundle_path: src/BrowserPicker.App/bin/Release/net9.0-windows/win-x64/publish
publish:
runs-on: ubuntu-latest
needs: [prepare, dependent, portable]
steps:
- - name: Checkout
- uses: actions/checkout@v4
- with:
- fetch-depth: 0
-
- - name: Retrieve artifacts
- uses: actions/download-artifact@v4
-
- - name: Package bundles
- run: |
- rm -rf *.zip
- for bundle in Dependent Portable; do
- (cd $bundle-${{ needs.prepare.outputs.version }}-Release; zip -r ../$bundle.zip *)
- done
-
- - name: Release
- uses: softprops/action-gh-release@v2
- with:
- generate_release_notes: true
- draft: true
- prerelease: true
- files: |
- DependentSetup-${{ needs.prepare.outputs.version }}-Release/BrowserPicker.msi
- PortableSetup-${{ needs.prepare.outputs.version }}-Release/BrowserPicker-Portable.msi
- Dependent.zip
- Portable.zip
+ - name: Checkout
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+
+ - name: Retrieve artifacts
+ uses: actions/download-artifact@v4
+
+ - name: Package bundles
+ run: |
+ rm -rf *.zip
+ for bundle in Dependent Portable; do
+ (cd $bundle-${{ needs.prepare.outputs.version }}-Release; zip -r ../$bundle.zip *)
+ done
+
+ - name: Release
+ uses: softprops/action-gh-release@v2
+ with:
+ tag_name: ${{ github.ref_name }} # 显式指定Tag名称
+ name: Release ${{ github.ref_name }} # 可选:设置Release标题
+ generate_release_notes: true
+ draft: true
+ prerelease: true
+ files: |
+ DependentSetup-${{ needs.prepare.outputs.version }}-Release/BrowserPicker.msi
+ PortableSetup-${{ needs.prepare.outputs.version }}-Release/BrowserPicker-Portable.msi
+ Dependent.zip
+ Portable.zip
diff --git a/.gitignore b/.gitignore
index 6143666..30253b9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,3 +3,6 @@ bin
obj
*.user
packages/
+/.history
+*.pubxml
+*.lnk
diff --git a/BrowserPicker.sln b/BrowserPicker.sln
index ed44324..e3ede8a 100644
--- a/BrowserPicker.sln
+++ b/BrowserPicker.sln
@@ -1,13 +1,14 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 17
-VisualStudioVersion = 17.0.31919.166
+# Visual Studio Version 18
+VisualStudioVersion = 18.1.11304.174
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{F46B5958-C607-4E07-9C2C-F63538149287}"
ProjectSection(SolutionItems) = preProject
LICENSE = LICENSE
nuget.config = nuget.config
readme.md = readme.md
+ Resources.Designer.t4 = Resources.Designer.t4
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BrowserPicker", "src\BrowserPicker\BrowserPicker.csproj", "{D7695535-9C0D-4983-B8F7-09B067347E7E}"
diff --git a/choose_version.md b/choose_version.md
new file mode 100644
index 0000000..a687452
--- /dev/null
+++ b/choose_version.md
@@ -0,0 +1,24 @@
+# 我应该选择哪个版本?
+
+你可在 [GitHub](https://github.com/XTsat/BrowserPicker_i18n/releases) 上获取最新版本
+
+## 依赖 .NET 运行时的安装包
+
+`NoDeps` 版本为即时编译(JIT)版本,需安装 [.NET 9.0 桌面运行时](https://dotnet.microsoft.com/en-us/download/dotnet/9.0) 才能使用。
+直接下载链接:[64 位系统](https://dotnet.microsoft.com/en-us/download/dotnet/thank-you/runtime-desktop-9.0.3-windows-x64-installer)、[32 位系统](https://dotnet.microsoft.com/en-us/download/dotnet/thank-you/runtime-desktop-9.0.3-windows-x86-installer)。
+
+## 便携版安装包
+
+`Portable` 版本包含适用于 win-x64 系统的可执行文件,可以不用安装直接使用。
+
+## 原生镜像生成
+
+安装过程中,`BrowserPicker.msi` 会执行 ngen 工具为你的电脑生成原生镜像,这能显著提升可执行文件的启动速度。若你选择便携版本,可运行命令 `ngen install BrowserPicker.exe` 以获得相同优化效果。
+
+
+
+## 手动编译
+
+`VS` 版本是手动编译的最小文件版本
diff --git a/docs/config_add_browser.png b/docs/config_add_browser.png
deleted file mode 100644
index be290b6..0000000
Binary files a/docs/config_add_browser.png and /dev/null differ
diff --git a/docs/config_add_browser_exe_picked.png b/docs/config_add_browser_exe_picked.png
deleted file mode 100644
index 7a0fd30..0000000
Binary files a/docs/config_add_browser_exe_picked.png and /dev/null differ
diff --git a/docs/config_behaviour.png b/docs/config_behaviour.png
deleted file mode 100644
index 24f16cf..0000000
Binary files a/docs/config_behaviour.png and /dev/null differ
diff --git a/docs/config_defaults_browsers.png b/docs/config_defaults_browsers.png
deleted file mode 100644
index 800e584..0000000
Binary files a/docs/config_defaults_browsers.png and /dev/null differ
diff --git a/docs/config_defaults_empty.png b/docs/config_defaults_empty.png
deleted file mode 100644
index be488c7..0000000
Binary files a/docs/config_defaults_empty.png and /dev/null differ
diff --git a/docs/config_defaults_match_type.png b/docs/config_defaults_match_type.png
deleted file mode 100644
index 0fe0254..0000000
Binary files a/docs/config_defaults_match_type.png and /dev/null differ
diff --git a/docs/config_defaults_test_no_match.png b/docs/config_defaults_test_no_match.png
deleted file mode 100644
index a037151..0000000
Binary files a/docs/config_defaults_test_no_match.png and /dev/null differ
diff --git a/docs/config_disabled.png b/docs/config_disabled.png
deleted file mode 100644
index 3f6d888..0000000
Binary files a/docs/config_disabled.png and /dev/null differ
diff --git a/docs/config_list.png b/docs/config_list.png
deleted file mode 100644
index 6f41500..0000000
Binary files a/docs/config_list.png and /dev/null differ
diff --git a/docs/config_list_with_notepad.png b/docs/config_list_with_notepad.png
deleted file mode 100644
index a40aa38..0000000
Binary files a/docs/config_list_with_notepad.png and /dev/null differ
diff --git a/docs/en/config_add_browser.png b/docs/en/config_add_browser.png
new file mode 100644
index 0000000..9f5680b
Binary files /dev/null and b/docs/en/config_add_browser.png differ
diff --git a/docs/en/config_add_browser_exe_picked.png b/docs/en/config_add_browser_exe_picked.png
new file mode 100644
index 0000000..ecca649
Binary files /dev/null and b/docs/en/config_add_browser_exe_picked.png differ
diff --git a/docs/en/config_behaviour.png b/docs/en/config_behaviour.png
new file mode 100644
index 0000000..dfb5394
Binary files /dev/null and b/docs/en/config_behaviour.png differ
diff --git a/docs/en/config_defaults_browsers.png b/docs/en/config_defaults_browsers.png
new file mode 100644
index 0000000..01f515e
Binary files /dev/null and b/docs/en/config_defaults_browsers.png differ
diff --git a/docs/en/config_defaults_empty.png b/docs/en/config_defaults_empty.png
new file mode 100644
index 0000000..6c74b01
Binary files /dev/null and b/docs/en/config_defaults_empty.png differ
diff --git a/docs/en/config_defaults_match_type.png b/docs/en/config_defaults_match_type.png
new file mode 100644
index 0000000..45f84bf
Binary files /dev/null and b/docs/en/config_defaults_match_type.png differ
diff --git a/docs/en/config_defaults_test_no_match.png b/docs/en/config_defaults_test_no_match.png
new file mode 100644
index 0000000..629a544
Binary files /dev/null and b/docs/en/config_defaults_test_no_match.png differ
diff --git a/docs/en/config_disabled.png b/docs/en/config_disabled.png
new file mode 100644
index 0000000..6ab1fcc
Binary files /dev/null and b/docs/en/config_disabled.png differ
diff --git a/docs/en/config_list.png b/docs/en/config_list.png
new file mode 100644
index 0000000..245dfc1
Binary files /dev/null and b/docs/en/config_list.png differ
diff --git a/docs/en/config_list_with_notepad.png b/docs/en/config_list_with_notepad.png
new file mode 100644
index 0000000..d9b1c1d
Binary files /dev/null and b/docs/en/config_list_with_notepad.png differ
diff --git a/docs/en/selector_edit_url.png b/docs/en/selector_edit_url.png
new file mode 100644
index 0000000..d188cd4
Binary files /dev/null and b/docs/en/selector_edit_url.png differ
diff --git a/docs/en/selector_edited_url.png b/docs/en/selector_edited_url.png
new file mode 100644
index 0000000..ffda228
Binary files /dev/null and b/docs/en/selector_edited_url.png differ
diff --git a/docs/en/selector_two_running.png b/docs/en/selector_two_running.png
new file mode 100644
index 0000000..a2bfe51
Binary files /dev/null and b/docs/en/selector_two_running.png differ
diff --git a/docs/selector_edit_url.png b/docs/selector_edit_url.png
deleted file mode 100644
index 45e9704..0000000
Binary files a/docs/selector_edit_url.png and /dev/null differ
diff --git a/docs/selector_edited_url.png b/docs/selector_edited_url.png
deleted file mode 100644
index bf6b636..0000000
Binary files a/docs/selector_edited_url.png and /dev/null differ
diff --git a/docs/selector_two_running.png b/docs/selector_two_running.png
deleted file mode 100644
index 1b84afa..0000000
Binary files a/docs/selector_two_running.png and /dev/null differ
diff --git a/docs/zh/config_add_browser.png b/docs/zh/config_add_browser.png
new file mode 100644
index 0000000..6c4aa60
Binary files /dev/null and b/docs/zh/config_add_browser.png differ
diff --git a/docs/zh/config_behaviour.png b/docs/zh/config_behaviour.png
new file mode 100644
index 0000000..8b11c08
Binary files /dev/null and b/docs/zh/config_behaviour.png differ
diff --git a/docs/zh/config_defaults_browsers.png b/docs/zh/config_defaults_browsers.png
new file mode 100644
index 0000000..ba0fc83
Binary files /dev/null and b/docs/zh/config_defaults_browsers.png differ
diff --git a/docs/zh/config_defaults_empty.png b/docs/zh/config_defaults_empty.png
new file mode 100644
index 0000000..f20aeda
Binary files /dev/null and b/docs/zh/config_defaults_empty.png differ
diff --git a/docs/zh/config_defaults_match_type.png b/docs/zh/config_defaults_match_type.png
new file mode 100644
index 0000000..296d40e
Binary files /dev/null and b/docs/zh/config_defaults_match_type.png differ
diff --git a/docs/zh/config_defaults_test_no_match.png b/docs/zh/config_defaults_test_no_match.png
new file mode 100644
index 0000000..0e49f4a
Binary files /dev/null and b/docs/zh/config_defaults_test_no_match.png differ
diff --git a/docs/zh/config_list.png b/docs/zh/config_list.png
new file mode 100644
index 0000000..67d015f
Binary files /dev/null and b/docs/zh/config_list.png differ
diff --git a/docs/zh/selector_edited_url.png b/docs/zh/selector_edited_url.png
new file mode 100644
index 0000000..d8b30d0
Binary files /dev/null and b/docs/zh/selector_edited_url.png differ
diff --git a/docs/zh/selector_two_running.png b/docs/zh/selector_two_running.png
new file mode 100644
index 0000000..89d0389
Binary files /dev/null and b/docs/zh/selector_two_running.png differ
diff --git a/readme.md b/readme.md
index 4883156..046aeb2 100644
--- a/readme.md
+++ b/readme.md
@@ -1,36 +1,46 @@
# Browser Picker
+
+English | [中文](/readme_zh.md)
+
A default browser replacement for windows to let you pick your preferred browser on the fly or in accordance with your own rules.
-
+
You can easily configure it to use Firefox for `github.com` and `slashdot.org`, but leave Edge to handle `microsoft.com`
and even let Internet Explorer handle that old internal LOB app you'd rather not use but must.
## Installation
+
You can find the latest release on [github](https://github.com/mortenn/BrowserPicker/releases).
### Default browser
-To enable the browser picker window, you need to set Browser Picker as your default browser.
+
+To enable the browser picker window, you need to set Browser Picker as your default browser.
### .NET Runtime dependent binary
+
BrowserPicker.msi and Dependent.zip are JIT compiled and require you have the [.NET 9.0 Desktop Runtime](https://dotnet.microsoft.com/en-us/download/dotnet/9.0) installed.
Direct links: [64bit systems](https://dotnet.microsoft.com/en-us/download/dotnet/thank-you/runtime-desktop-9.0.3-windows-x64-installer), [32bit systems](https://dotnet.microsoft.com/en-us/download/dotnet/thank-you/runtime-desktop-9.0.3-windows-x86-installer).
#### Native image generation
+
As part of installation, `BrowserPicker.msi` will execute ngen to build a native image for your computer.
This significantly enhances launch times for the executable.
If you prefer the bundle, you may run `ngen install BrowserPicker.exe` to get the same benefit.
### Portable binary
+
If you do not want to have the .net runtime installed on your computer, you may download the Portable version, which includes the runtime.
`BrowserPicker-Portable.msi` and `Portable.zip` contain a win-x64 binary executable with embedded .NET runtime.
This makes the file sizes quite significantly larger, but you do not need an additional runtime to use these.
### Signing certificate
+
To avoid warnings about unknown publisher, you may [import](https://stackoverflow.com/questions/49039136/powershell-script-to-install-trusted-publisher-certificates) the provided certificate into your certificate store first.
### Manual steps
+
You need to open the settings app from the start menu, navigate into Apps, select Default apps, then change the Web browser to BrowserPicker.
Please ensure BrowserPicker can be started before you do this.
@@ -42,19 +52,21 @@ When you open a link outside a browser, one of these things will happen, in orde
2. If you have set up a configuration rule matching the url being opened, the selected browser will be launched with the url.
3. If you only have one browser running, the link will be opened in that browser.
4. If you have configured a default browser, it will be asked to open the url.
-3. Otherwise, you will be presented with a simple window asking you which browser you want to use.
+5. Otherwise, you will be presented with a simple window asking you which browser you want to use.
The url is shown at the top of the window, and if it matches a list of known url shorteners, BrowserPicker will expand this address and show you the real one after a short delay.
If you do not want BrowserPicker to perform this operation (it will call the internet), you may disable this feature in the settings.
### Copy url
+
You can click the clipboard icon at the top to copy the url without opening it
### Edit url
+
You can click the pencil icon at the top of the window to edit or copy the url before visiting it or cancelling:
-
-
+
+
### Keyboard shortcuts
@@ -84,38 +96,43 @@ As you use the application, it keeps count of how many times you selected each b
At the bottom of the window, there is a checkbox to enable "always ask" and a hyperlink to open settings.
## Settings
+
By simply launching BrowserPicker from the start menu or double clicking the `BrowserPicker.exe` file, you will be presented with a GUI to configure the behaviour.
The configuration is saved in the Windows registry: `HKEY_CURRENT_USER\Software\BrowserPicker`, if you ever need to manually edit it or make a backup.
-
+
### Browsers
The browser list shows you the browsers BrowserPicker has been configured or detected to use.
#### Disabling browsers
+
You can disable a browser by clicking `Enabled`, this will hide the browser from the selection list.
-
+
#### Removing browsers
+
If you click the red X, you may remove a browser.
Do note that if it was automatically detected, it will return to the list the next time auto configuration is performed.
#### Automatic configuration
+
The `Refresh broser list` function gets automatically executed in the background when you use BrowserPicker.
This helps it discovering newly installed browsers, in case a new browser has been installed,
#### Manually adding browser
+
You may click the hyperlink `Add browser` to open a popup where you may manually add a browser that has not been detected - or some other tool that isn't a browser.
You can click the buttons behind the input boxes to bring up the file picker interface of windows to select the executable or icon file you want to use.
-
-
+
+
-
+
If you browse for the command first, the application will assume the executable also has an icon, and prefill that box.
@@ -124,7 +141,7 @@ The name of the application will be attempted to be set automatically based on i
##### Chrome profiles
Tip for Chrome Users: If you are using multiple Chrome profiles, by default if you choose Chrome it will launch in the last
-profile you launched Chrome with. To make it possibe for browser picker to select a profile you can create a new browser
+profile you launched Chrome with. To make it possibe for browser picker to select a profile you can create a new browser
for each profile, set the program to the chrome executable, and add a command line argument to specify which profile to launch:
`--profile-directory=Default` for the first profile, `--profile-directory="Profile 1"` for the second profile, and so on.
@@ -135,9 +152,10 @@ Please note that arguments with spaces do require "" around them to be properly
Similar configuration should be possible for firefox.
### Behaviour
+
This tab contains various settings that govern how BrowserPicker operates.
-
+
> [ ] Turn off transparency
@@ -156,8 +174,7 @@ When configured, BrowserPicker will always use this browser unless a default bro
This option makes it so BrowserPicker will only pick matched default browsers and otherwise show the selection window.
> [ ] Disable url resolution
-
-
+>
> [ ] Ignore defaults when browser is not running
When enabled, configured default browsers only apply when they are already running.
@@ -176,37 +193,45 @@ This option turns this feature off, preventing BrowserPicker to call the network
You may adjust for how long BrowserPicker attempts to resolve an url here.
### Defaults
+
The defaults tab lets you configure rules to map certain urls to certain browsers.
-
+
+
+#### Match types
-##### Match types
There exists four different match types, but you cannot use Default, that is reserved for use elsewhere.
The option will eventually get hidden in the interface, but for now it becomes Hostname when selected.
-
+
+
+##### Hostname match
-###### Hostname match
The pattern will match the end of the hostname part of the url, ie. `hub.com` would match `https://www.github.com/mortenn/BrowserPicker`, but not `https://example.com/cgi-bin/hub.com`
-###### Prefix match
+##### Prefix match
+
The pattern will match the beginning of the url, ie. `https://github.com/mortenn` would match `https://github.com/mortenn/BrowserPicker` but not `https://www.github.com/mortenn/BrowserPicker`
-###### Regex match
+##### Regex match
+
The pattern is a .NET regular expression and will be executed against the url, see [.NET regular expressions](https://learn.microsoft.com/en-us/dotnet/standard/base-types/regular-expressions) for details.
-##### Browser
+#### Browser
+
The selected browser will be the one to launch for matched urls.
-
+
### Test defaults
+
There is even a handy dandy tool for verifying your settings,
just paste that url into the big white text box and get instant feedback on the browser selection process:
-
+
### Logging
+
BrowserPicker uses ILogger with EventLog support.
To get detailed logs, please either change appsettings.json or set the environment variable `Logging__EventLog__LogLevel__BrowserPicker` to either `Information` or `Debug`
@@ -216,4 +241,4 @@ If you are using the archived version rather than the installer package,
you will need to run this powershell command before logs will appear:
```New-EventLog -LogName Application -Source BrowserPicker```
-`
+`
diff --git a/readme_zh.md b/readme_zh.md
new file mode 100644
index 0000000..2f3bd70
--- /dev/null
+++ b/readme_zh.md
@@ -0,0 +1,216 @@
+# 浏览器选择器(Browser Picker)
+
+[English](/readme.md) | 中文
+
+一款适用于 Windows 系统的默认浏览器替代工具,可让你选择偏好的浏览器,或根据自定义规则自动选择浏览器。
+
+
+
+你可以轻松配置规则:例如用 Firefox 打开 `GitHub.com` 和 `slashdot.org`,用 Edge 处理 `microsoft.com`,甚至可以让 Internet Explorer 打开那款你不想用但又必须使用的旧版内部业务线(LOB)应用。
+
+## 安装
+
+你可在 [GitHub](https://github.com/XTsat/BrowserPicker_i18n/releases) 上获取最新版本。
+
+### 设置默认浏览器
+
+要启用浏览器选择器窗口,需将 “浏览器选择器” 设为系统默认浏览器。
+
+手动设置步骤:
+
+1.从开始菜单打开 “设置” 应用;
+2.进入 “应用” 选项;
+3.选择 “默认应用”;
+4.将 “网页浏览器” 改为 “浏览器选择器”。
+ **注意**:执行此操作前,请确保 “浏览器选择器” 可正常启动。
+
+## 使用方法
+
+当你在浏览器外打开链接时,会按以下优先级触发操作:
+
+1. 若此前选择过 **始终询问**,则显示浏览器选择窗口
+2. 若已配置与当前链接匹配的规则,则用指定浏览器打开该链接
+3. 若仅有一个浏览器处于运行状态,则在该浏览器中打开链接
+4. 若已配置默认浏览器,则调用默认浏览器打开链接
+5. 若以上情况均不满足,则显示简易窗口,让你选择要使用的浏览器
+
+链接地址会显示在窗口顶部:若该地址属于已知短链接服务,浏览器选择器会在短暂延迟后解析真实地址并显示。
+若你不希望浏览器选择器执行此操作(该操作需连接网络),可在设置中禁用此功能。
+
+### 复制链接
+
+点击窗口顶部的剪贴板图标,可复制链接地址而不打开链接。
+
+### 编辑链接
+
+点击窗口顶部的铅笔图标,可在访问或取消访问链接前编辑或复制链接地址:
+
+
+
+### 键盘快捷键
+
+当浏览器选择窗口处于打开并获得焦点的状态时,可使用以下快捷键:
+按住 [Alt] 键并按下数字键,可在隐私模式下打开对应浏览器。
+
+- [Enter] 或 [1]:选择列表中的第一个浏览器
+- [2]:选择列表中的第二个浏览器
+- ...
+- [9]:选择列表中的第九个浏览器
+- [Esc]:取消操作并关闭窗口
+
+若点击窗口外部导致窗口失去焦点,窗口会关闭且不会用任何浏览器打开链接。
+
+当前处于运行状态的浏览器,其名称会以粗体显示;未运行的浏览器,名称会以斜体显示。
+
+在你使用该应用的过程中,它会统计你选择各浏览器的次数,并根据这些数据自动按你的偏好顺序排列浏览器列表。
+
+窗口底部有一个 **始终询问** 复选框,以及一个用于打开设置界面的超链接。
+
+## 设置
+
+从开始菜单启动 “浏览器选择器”,或双击 `BrowserPicker.exe` 文件,即可打开图形化界面(GUI)配置其行为。
+
+配置信息保存在 Windows 注册表中,路径为 `HKEY_CURRENT_USER\Software\BrowserPicker`,你可在此路径下手动编辑配置或创建备份。
+
+
+
+### 浏览器
+
+浏览器列表显示 “浏览器选择器” 已配置或检测到的可用浏览器。
+
+#### 禁用浏览器
+
+点击 **已启用(Enabled)** 可禁用对应浏览器,禁用后该浏览器会从选择列表中隐藏。
+
+#### 删除浏览器
+
+点击红色 “X” 图标可删除对应浏览器。
+
+注意:若该浏览器是自动检测到的,下次执行自动配置时会重新出现在列表中。
+
+#### 自动配置
+
+使用 “浏览器选择器” 时,**刷新浏览器列表** 功能会在后台自动执行,帮助检测新安装的浏览器。
+
+#### 手动添加浏览器
+
+点击 **添加浏览器** 超链接,会弹出一个窗口,你可在此手动添加未被检测到的浏览器,或添加非浏览器类工具。
+点击输入框后方的按钮,可打开 Windows 文件选择界面,选择要使用的可执行文件或图标文件。
+
+
+
+若你先选择可执行文件,应用会默认该文件包含图标,并自动填充图标文件输入框。
+
+应用名称会尝试根据可执行文件中的信息自动设置。
+
+##### chromium 配置文件
+
+chromium 内核浏览器用户提示:若你使用多个配置文件,默认情况下选择浏览器时,会使用上次启动时的配置文件。
+
+若想让浏览器选择器能指定配置文件,可为每个配置文件创建一个 “新浏览器” 条目:将程序路径设为 Chrome / Edge 可执行文件,并添加命令行参数指定要启动的配置文件,例如:
+
+- 第一个配置文件:`--profile-directory=Default`
+- 第二个配置文件:`--profile-directory="Profile 1"`
+- 第三个配置文件:`--profile-directory="Profile 2"`
+ 以此类推。
+
+注意:含空格的参数需用英文双引号(" ")包裹,才能正确传递给 Chrome。
+
+##### Firefox 配置文件
+
+Firefox 也支持类似的配置文件设置。
+
+### 设置
+
+此标签页包含控制 “浏览器选择器” 运行方式的各类设置。
+
+
+
+> [ ] 使用纯黑背景
+
+勾选后,浏览器选择器会使用纯色黑色背景。
+
+> [ ] 始终显示浏览器选择窗口
+
+此选项在浏览器选择窗口中也可设置。启用后,浏览器选择器会始终提示你选择浏览器。
+
+> [ ] 当无匹配链接的默认浏览器时,使用:[__v]
+
+配置后,除非为当前链接设置了默认浏览器,否则浏览器选择器会始终使用此处指定的浏览器。
+
+> [ ] 即使浏览器未运行也使用默认设置
+
+启用此选项后,浏览器选择器仅会使用匹配的默认浏览器;若无匹配项,则显示选择窗口。
+
+> [ ] 禁用链接解析
+>
+> [ ] 浏览器未运行时忽略默认设置
+
+启用后,已配置的默认浏览器仅在处于运行状态时才生效。
+
+> [ ] 根据使用情况更新浏览器列表顺序
+
+启用后,浏览器列表会根据你的选择频率自动排序。
+
+> [ ] 禁止网络活动
+
+浏览器选择器可能会通过 DNS 查询和 HTTP 请求探测链接,以检查该链接是否存在重定向。
+
+勾选此选项可关闭该功能,避免浏览器选择器在打开链接时连接网络。
+
+> 链接解析超时时间:[___]
+
+在此处调整浏览器选择器尝试解析链接的最长时间。
+
+### 浏览器匹配
+
+此标签页可配置规则,将特定链接与指定浏览器关联。
+
+
+
+#### 匹配类型
+
+共有四种匹配类型,其中 **默认(Default)** 类型为预留选项,暂不可用。该选项最终会从界面中隐藏,目前选择后会自动转为 **主机名(Hostname)** 匹配类型。
+
+
+
+##### 主机名匹配(Hostname match)
+
+规则会匹配链接 **主机名** 部分的末尾。
+
+例如,`hub.com` 会匹配 `https://www.github.com/mortenn/BrowserPicker`,但不会匹配 `https://example.com/cgi-bin/hub.com`
+
+##### 前缀匹配(Prefix match)
+
+规则会匹配链接的开头。例如,`https://github.com/mortenn` 会匹配 `https://github.com/mortenn/BrowserPicker`,但不会匹配 `https://www.github.com/mortenn/BrowserPicker`
+
+##### 正则表达式匹配(Regex match)
+
+规则使用 .NET 正则表达式,会对整个链接执行匹配。详情可参考 [.NET 正则表达式文档](https://learn.microsoft.com/en-us/dotnet/standard/base-types/regular-expressions)。
+
+#### 匹配浏览器
+
+选择匹配链接后要启动的浏览器。
+
+
+
+### 测试默认规则
+
+界面提供了便捷的规则验证工具:
+在白色文本框中粘贴链接,即可即时查看浏览器选择结果(验证规则是否生效):
+
+
+
+### 日志
+
+浏览器选择器使用 ILogger 记录日志,并支持事件日志(EventLog)功能。
+
+如需详细日志,可通过以下两种方式操作:
+
+- 修改 appsettings.json 文件
+- 将环境变量 `Logging__EventLog__LogLevel__BrowserPicker` 设置为 **Information(信息级)** 或 **Debug(调试级)**。
+
+默认情况下,仅记录警告(Warning)及更高级别的事件。
+
+若你使用的是压缩包版本(非安装包),需先运行以下 PowerShell 命令,日志才能正常显示:
+`New-EventLog -LogName Application -Source BrowserPicker`
diff --git a/src/BrowserPicker.App/App.config b/src/BrowserPicker.App/App.config
new file mode 100644
index 0000000..3be2f22
--- /dev/null
+++ b/src/BrowserPicker.App/App.config
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/BrowserPicker.App/App.xaml b/src/BrowserPicker.App/App.xaml
index 6c31584..c7adb00 100644
--- a/src/BrowserPicker.App/App.xaml
+++ b/src/BrowserPicker.App/App.xaml
@@ -1,4 +1,4 @@
-
-
+
diff --git a/src/BrowserPicker.App/App.xaml.cs b/src/BrowserPicker.App/App.xaml.cs
index 1074164..920103b 100644
--- a/src/BrowserPicker.App/App.xaml.cs
+++ b/src/BrowserPicker.App/App.xaml.cs
@@ -1,5 +1,9 @@
using System;
using System.Collections.Generic;
+using System.Configuration;
+using System.Data;
+using System.Diagnostics;
+using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading;
@@ -8,6 +12,7 @@
using System.Windows.Threading;
using BrowserPicker.View;
using BrowserPicker.ViewModel;
+using static BrowserPicker.ViewModel.ApplicationViewModel;
namespace BrowserPicker;
@@ -41,6 +46,10 @@ private class InvalidUTF8Patch : EncodingProvider
public App()
{
+ var culture = new CultureInfo("en-US");
+ Thread.CurrentThread.CurrentCulture = culture;
+ Thread.CurrentThread.CurrentUICulture = culture;
+
Encoding.RegisterProvider(new InvalidUTF8Patch());
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
BackgroundTasks.Add(Settings);
@@ -67,10 +76,29 @@ public App()
protected override void OnStartup(StartupEventArgs e)
{
+ base.OnStartup(e);
+ LoadSavedLanguage();
var worker = StartupBackgroundTasks();
worker.ContinueWith(CheckBackgroundTasks);
}
+ private void LoadSavedLanguage()
+ {
+ try
+ {
+ var savedLanguage = AppConfigHelper.ReadAppConfig("language");
+ if (!string.IsNullOrEmpty(savedLanguage))
+ {
+ var culture = new CultureInfo(savedLanguage);
+ Thread.CurrentThread.CurrentUICulture = culture;
+ Thread.CurrentThread.CurrentCulture = culture;
+ }
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine($"Failed to load saved language: {ex.Message}");
+ }
+ }
///
/// This method should never be called, as the StartupBackgroundTasks has robust exception handling
///
diff --git a/src/BrowserPicker.App/BrowserPicker.App.csproj b/src/BrowserPicker.App/BrowserPicker.App.csproj
index 3a48c3b..0c33ee5 100644
--- a/src/BrowserPicker.App/BrowserPicker.App.csproj
+++ b/src/BrowserPicker.App/BrowserPicker.App.csproj
@@ -10,6 +10,7 @@
Resources\web_icon.ico
False
enable
+
@@ -28,6 +29,15 @@
+
+ True
+ True
+ i18n.resx
+
+
+ PublicResXFileCodeGenerator
+ i18n.Designer.cs
+
MSBuild:Compile
Wpf
diff --git a/src/BrowserPicker.App/Resources/i18n.Designer.cs b/src/BrowserPicker.App/Resources/i18n.Designer.cs
new file mode 100644
index 0000000..52d9894
--- /dev/null
+++ b/src/BrowserPicker.App/Resources/i18n.Designer.cs
@@ -0,0 +1,1033 @@
+//------------------------------------------------------------------------------
+//
+// 此代码由工具生成。
+// 运行时版本:4.0.30319.42000
+//
+// 对此文件的更改可能会导致不正确的行为,并且如果
+// 重新生成代码,这些更改将会丢失。
+//
+//------------------------------------------------------------------------------
+
+namespace BrowserPicker.Resources {
+ using System;
+
+
+ ///
+ /// 一个强类型的资源类,用于查找本地化的字符串等。
+ ///
+ // 此类是由 StronglyTypedResourceBuilder
+ // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。
+ // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen
+ // (以 /str 作为命令选项),或重新生成 VS 项目。
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "18.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ public class i18n {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal i18n() {
+ }
+
+ ///
+ /// 返回此类使用的缓存的 ResourceManager 实例。
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ public static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("BrowserPicker.Resources.i18n", typeof(i18n).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// 重写当前线程的 CurrentUICulture 属性,对
+ /// 使用此强类型资源类的所有资源查找执行重写。
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ public static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ ///
+ /// 查找类似 Expand file:// urls 的本地化字符串。
+ ///
+ public static string BoxAddBrowserBoxExpandUrls {
+ get {
+ return ResourceManager.GetString("BoxAddBrowserBoxExpandUrls", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Command: 的本地化字符串。
+ ///
+ public static string BoxAddBrowserCommand {
+ get {
+ return ResourceManager.GetString("BoxAddBrowserCommand", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Command Args: 的本地化字符串。
+ ///
+ public static string BoxAddBrowserCommandArgs {
+ get {
+ return ResourceManager.GetString("BoxAddBrowserCommandArgs", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Disable automatic updates 的本地化字符串。
+ ///
+ public static string BoxAddBrowserDisableUpdates {
+ get {
+ return ResourceManager.GetString("BoxAddBrowserDisableUpdates", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Hotkey: 的本地化字符串。
+ ///
+ public static string BoxAddBrowserHotkey {
+ get {
+ return ResourceManager.GetString("BoxAddBrowserHotkey", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 (Automatic) 的本地化字符串。
+ ///
+ public static string BoxAddBrowserHotkeyTip {
+ get {
+ return ResourceManager.GetString("BoxAddBrowserHotkeyTip", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Icon: 的本地化字符串。
+ ///
+ public static string BoxAddBrowserIcon {
+ get {
+ return ResourceManager.GetString("BoxAddBrowserIcon", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Name: 的本地化字符串。
+ ///
+ public static string BoxAddBrowserName {
+ get {
+ return ResourceManager.GetString("BoxAddBrowserName", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Privacy Args: 的本地化字符串。
+ ///
+ public static string BoxAddBrowserPrivacyArgs {
+ get {
+ return ResourceManager.GetString("BoxAddBrowserPrivacyArgs", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Browse file locations 的本地化字符串。
+ ///
+ public static string BoxAddBrowserTipBrowseLocations {
+ get {
+ return ResourceManager.GetString("BoxAddBrowserTipBrowseLocations", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Copy to clipboard 的本地化字符串。
+ ///
+ public static string BoxCrashedClipboard {
+ get {
+ return ResourceManager.GetString("BoxCrashedClipboard", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Unfortunately, BrowserPicker has crashed. 的本地化字符串。
+ ///
+ public static string BoxCrashedText1 {
+ get {
+ return ResourceManager.GetString("BoxCrashedText1", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Please submit a bug report on https://github.com/mortenn/BrowserPicker. 的本地化字符串。
+ ///
+ public static string BoxCrashedText2 {
+ get {
+ return ResourceManager.GetString("BoxCrashedText2", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Exception details: 的本地化字符串。
+ ///
+ public static string BoxCrashedTextInfo {
+ get {
+ return ResourceManager.GetString("BoxCrashedTextInfo", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Scanning URL... 的本地化字符串。
+ ///
+ public static string BoxLoadingScanningURL {
+ get {
+ return ResourceManager.GetString("BoxLoadingScanningURL", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Always ask 的本地化字符串。
+ ///
+ public static string BoxOpenBrowserBoxAlwaysAsk {
+ get {
+ return ResourceManager.GetString("BoxOpenBrowserBoxAlwaysAsk", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Remember my choice for 的本地化字符串。
+ ///
+ public static string BoxOpenBrowserBoxRememberChoice {
+ get {
+ return ResourceManager.GetString("BoxOpenBrowserBoxRememberChoice", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Configure browser list 的本地化字符串。
+ ///
+ public static string BoxOpenBrowserBtnConfigure {
+ get {
+ return ResourceManager.GetString("BoxOpenBrowserBtnConfigure", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Redirected URL 的本地化字符串。
+ ///
+ public static string BoxOpenBrowserRedirectedUrl {
+ get {
+ return ResourceManager.GetString("BoxOpenBrowserRedirectedUrl", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Copy the URL to the clipboard 的本地化字符串。
+ ///
+ public static string BoxOpenBrowserTipCopyUrl {
+ get {
+ return ResourceManager.GetString("BoxOpenBrowserTipCopyUrl", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Edit the URL before opening it 的本地化字符串。
+ ///
+ public static string BoxOpenBrowserTipEditUrl {
+ get {
+ return ResourceManager.GetString("BoxOpenBrowserTipEditUrl", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Pins the window, making it not close on lost focus 的本地化字符串。
+ ///
+ public static string BoxOpenBrowserTipPin {
+ get {
+ return ResourceManager.GetString("BoxOpenBrowserTipPin", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Permission prompt 的本地化字符串。
+ ///
+ public static string CsConfigAdminTip {
+ get {
+ return ResourceManager.GetString("CsConfigAdminTip", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Normal user permission restricted: {0}
+ ///Please turn off the security software and try again (no admin required) 的本地化字符串。
+ ///
+ public static string CsConfigAdminTipText {
+ get {
+ return ResourceManager.GetString("CsConfigAdminTipText", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Unable to get application filename 的本地化字符串。
+ ///
+ public static string CsConfigConfigurationNameErorr {
+ get {
+ return ResourceManager.GetString("CsConfigConfigurationNameErorr", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Web link selector 的本地化字符串。
+ ///
+ public static string CsConfigDefaultProgramRegistration {
+ get {
+ return ResourceManager.GetString("CsConfigDefaultProgramRegistration", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Extension or ProgID cannot be empty 的本地化字符串。
+ ///
+ public static string CsConfigFileTypeRegistration {
+ get {
+ return ResourceManager.GetString("CsConfigFileTypeRegistration", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Open{0}file 的本地化字符串。
+ ///
+ public static string CsConfigFileTypeRegistrationSuccessful {
+ get {
+ return ResourceManager.GetString("CsConfigFileTypeRegistrationSuccessful", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Unable to open link 的本地化字符串。
+ ///
+ public static string CsConfigHyperlinkError {
+ get {
+ return ResourceManager.GetString("CsConfigHyperlinkError", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 The application needs to restart to apply the new language settings. 的本地化字符串。
+ ///
+ public static string CsConfigLanguageChangeRestartMessage {
+ get {
+ return ResourceManager.GetString("CsConfigLanguageChangeRestartMessage", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Open with {0} 的本地化字符串。
+ ///
+ public static string CsConfigProgramRegistrationOpen {
+ get {
+ return ResourceManager.GetString("CsConfigProgramRegistrationOpen", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Open{0}link with{1} 的本地化字符串。
+ ///
+ public static string CsConfigProgramRegistrationOpen2 {
+ get {
+ return ResourceManager.GetString("CsConfigProgramRegistrationOpen2", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Registration failed: {0}
+ ///Please check if the app is blocked by security software 的本地化字符串。
+ ///
+ public static string CsConfigRegistrationFailed {
+ get {
+ return ResourceManager.GetString("CsConfigRegistrationFailed", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Registration successful!
+ ///1. Create a→ new desktop→ shortcut→ Enter the URL→ Right-click to→ open the method→ Select another app→ Find this program
+ ///2. Right click any .html file→ Choose another app→ More apps→ Find this program
+ ///3. If it is not displayed, it will take effect automatically after restarting the computer 的本地化字符串。
+ ///
+ public static string CsConfigRegistrationSuccessful {
+ get {
+ return ResourceManager.GetString("CsConfigRegistrationSuccessful", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Failed to restart the explorer, it is recommended to restart the computer to take effect 的本地化字符串。
+ ///
+ public static string CsConfigRestartExplorerFailTip {
+ get {
+ return ResourceManager.GetString("CsConfigRestartExplorerFailTip", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Do you want to restart Explorer for immediate effect?
+ ///The desktop icon will disappear temporarily during the restart process and resume automatically after a few seconds. 的本地化字符串。
+ ///
+ public static string CsConfigRestartExplorerTip {
+ get {
+ return ResourceManager.GetString("CsConfigRestartExplorerTip", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Remove failure: 的本地化字符串。
+ ///
+ public static string CsConfigUnregisterFailed {
+ get {
+ return ResourceManager.GetString("CsConfigUnregisterFailed", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Successfully removed all open method associations! 的本地化字符串。
+ ///
+ public static string CsConfigUnregisterSuccessful {
+ get {
+ return ResourceManager.GetString("CsConfigUnregisterSuccessful", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Are you sure you want to remove all open method associations?
+ ///Once removed, it will not be possible to quickly open the webpage through this program. 的本地化字符串。
+ ///
+ public static string CsConfigUnregisterTip {
+ get {
+ return ResourceManager.GetString("CsConfigUnregisterTip", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Export BrowserPicker settings 的本地化字符串。
+ ///
+ public static string NameBackupBtnExport {
+ get {
+ return ResourceManager.GetString("NameBackupBtnExport", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Import BrowserPicker settings 的本地化字符串。
+ ///
+ public static string NameBackupBtnImport {
+ get {
+ return ResourceManager.GetString("NameBackupBtnImport", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Back up or restore your BrowserPicker configuration: 的本地化字符串。
+ ///
+ public static string NameBackupText {
+ get {
+ return ResourceManager.GetString("NameBackupText", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Add browser 的本地化字符串。
+ ///
+ public static string NameBrowsersBtnAddBrowser {
+ get {
+ return ResourceManager.GetString("NameBrowsersBtnAddBrowser", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Disabled 的本地化字符串。
+ ///
+ public static string NameBrowsersBtnDisabled {
+ get {
+ return ResourceManager.GetString("NameBrowsersBtnDisabled", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Edit 的本地化字符串。
+ ///
+ public static string NameBrowsersBtnEdit {
+ get {
+ return ResourceManager.GetString("NameBrowsersBtnEdit", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Edit browser definition 的本地化字符串。
+ ///
+ public static string NameBrowsersBtnEditTip {
+ get {
+ return ResourceManager.GetString("NameBrowsersBtnEditTip", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Enabled 的本地化字符串。
+ ///
+ public static string NameBrowsersBtnEnabled {
+ get {
+ return ResourceManager.GetString("NameBrowsersBtnEnabled", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Refresh browser list 的本地化字符串。
+ ///
+ public static string NameBrowsersBtnRefreshBrowserList {
+ get {
+ return ResourceManager.GetString("NameBrowsersBtnRefreshBrowserList", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 X 的本地化字符串。
+ ///
+ public static string NameBrowsersBtnRemove {
+ get {
+ return ResourceManager.GetString("NameBrowsersBtnRemove", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Remove browser from list 的本地化字符串。
+ ///
+ public static string NameBrowsersBtnRemoveTip {
+ get {
+ return ResourceManager.GetString("NameBrowsersBtnRemoveTip", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Browser 的本地化字符串。
+ ///
+ public static string NameDefaultsMatchHeaderBrowser {
+ get {
+ return ResourceManager.GetString("NameDefaultsMatchHeaderBrowser", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Match by 的本地化字符串。
+ ///
+ public static string NameDefaultsMatchHeaderMatch {
+ get {
+ return ResourceManager.GetString("NameDefaultsMatchHeaderMatch", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Pattern 的本地化字符串。
+ ///
+ public static string NameDefaultsMatchHeaderPattern {
+ get {
+ return ResourceManager.GetString("NameDefaultsMatchHeaderPattern", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Set default browser per domain 的本地化字符串。
+ ///
+ public static string NameDefaultsMatchTextSetDefaultBrowser {
+ get {
+ return ResourceManager.GetString("NameDefaultsMatchTextSetDefaultBrowser", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Always show browser selection window 的本地化字符串。
+ ///
+ public static string NameSettingBoxAlwaysShow {
+ get {
+ return ResourceManager.GetString("NameSettingBoxAlwaysShow", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 When no default is configured matching the url, use: 的本地化字符串。
+ ///
+ public static string NameSettingBoxDefaultMatching {
+ get {
+ return ResourceManager.GetString("NameSettingBoxDefaultMatching", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Use defaults even when browser is not running 的本地化字符串。
+ ///
+ public static string NameSettingBoxDefaultsNotRunning {
+ get {
+ return ResourceManager.GetString("NameSettingBoxDefaultsNotRunning", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Disallow network activity 的本地化字符串。
+ ///
+ public static string NameSettingBoxDisallowNetworkActivity {
+ get {
+ return ResourceManager.GetString("NameSettingBoxDisallowNetworkActivity", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Always ask when no default is matching url 的本地化字符串。
+ ///
+ public static string NameSettingBoxNoDefaultMatching {
+ get {
+ return ResourceManager.GetString("NameSettingBoxNoDefaultMatching", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Turn off transparency 的本地化字符串。
+ ///
+ public static string NameSettingBoxTransparency {
+ get {
+ return ResourceManager.GetString("NameSettingBoxTransparency", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Set default browser 的本地化字符串。
+ ///
+ public static string NameSettingBtnDefaultBrowser {
+ get {
+ return ResourceManager.GetString("NameSettingBtnDefaultBrowser", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Register the open method 的本地化字符串。
+ ///
+ public static string NameSettingBtnRegAddOpen {
+ get {
+ return ResourceManager.GetString("NameSettingBtnRegAddOpen", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Remove the open method 的本地化字符串。
+ ///
+ public static string NameSettingBtnRegRemoveOpen {
+ get {
+ return ResourceManager.GetString("NameSettingBtnRegRemoveOpen", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Sort list alphabetically 的本地化字符串。
+ ///
+ public static string NameSettingBtnSortAlphabetically {
+ get {
+ return ResourceManager.GetString("NameSettingBtnSortAlphabetically", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Sort list manually 的本地化字符串。
+ ///
+ public static string NameSettingBtnSortManually {
+ get {
+ return ResourceManager.GetString("NameSettingBtnSortManually", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Sort list based on usage 的本地化字符串。
+ ///
+ public static string NameSettingBtnSortUsage {
+ get {
+ return ResourceManager.GetString("NameSettingBtnSortUsage", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 URL resolution timeout: 的本地化字符串。
+ ///
+ public static string NameSettingTextBoxUrlResolutionTimeout {
+ get {
+ return ResourceManager.GetString("NameSettingTextBoxUrlResolutionTimeout", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Actual browser choice: 的本地化字符串。
+ ///
+ public static string NameTestDefaultsTextActualChoice {
+ get {
+ return ResourceManager.GetString("NameTestDefaultsTextActualChoice", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Default browser matched: 的本地化字符串。
+ ///
+ public static string NameTestDefaultsTextDefaultMatched {
+ get {
+ return ResourceManager.GetString("NameTestDefaultsTextDefaultMatched", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Url: 的本地化字符串。
+ ///
+ public static string NameTestDefaultsTextUri {
+ get {
+ return ResourceManager.GetString("NameTestDefaultsTextUri", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Additional shorteners 的本地化字符串。
+ ///
+ public static string NameUrlShortenersHeaderAdditional {
+ get {
+ return ResourceManager.GetString("NameUrlShortenersHeaderAdditional", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Default shorteners 的本地化字符串。
+ ///
+ public static string NameUrlShortenersHeaderDefault {
+ get {
+ return ResourceManager.GetString("NameUrlShortenersHeaderDefault", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 ℹ️ Some additional jump pages not shown above 的本地化字符串。
+ ///
+ public static string NameUrlShortenersTextDefault {
+ get {
+ return ResourceManager.GetString("NameUrlShortenersTextDefault", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Some pages, such as outlook safelinks, have special handling where the target url is in the query string, these are not listed in this list, as they require additional information in code. 的本地化字符串。
+ ///
+ public static string NameUrlShortenersTextTipDefault {
+ get {
+ return ResourceManager.GetString("NameUrlShortenersTextTipDefault", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 These domains are known URL shorteners and gets probed on launch unless disallowed by settings 的本地化字符串。
+ ///
+ public static string NameUrlShortenersTextTitle {
+ get {
+ return ResourceManager.GetString("NameUrlShortenersTextTitle", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Welcome to BrowserPicker! 的本地化字符串。
+ ///
+ public static string NameWelcomeText1 {
+ get {
+ return ResourceManager.GetString("NameWelcomeText1", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 This message will automatically disappear after 10 seconds 的本地化字符串。
+ ///
+ public static string NameWelcomeText10 {
+ get {
+ return ResourceManager.GetString("NameWelcomeText10", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Here is the software settings window, 的本地化字符串。
+ ///
+ public static string NameWelcomeText2 {
+ get {
+ return ResourceManager.GetString("NameWelcomeText2", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 This software can select a customized browser when opening a link 的本地化字符串。
+ ///
+ public static string NameWelcomeText3 {
+ get {
+ return ResourceManager.GetString("NameWelcomeText3", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 If you think this software is helpful to you 的本地化字符串。
+ ///
+ public static string NameWelcomeText4 {
+ get {
+ return ResourceManager.GetString("NameWelcomeText4", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Welcome to GitHub give a star☆ 的本地化字符串。
+ ///
+ public static string NameWelcomeText5 {
+ get {
+ return ResourceManager.GetString("NameWelcomeText5", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 If you encounter software problems or have new ideas, please visit the project to submit an issue! 的本地化字符串。
+ ///
+ public static string NameWelcomeText6 {
+ get {
+ return ResourceManager.GetString("NameWelcomeText6", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 https://github.com/mortenn/BrowserPicker 的本地化字符串。
+ ///
+ public static string NameWelcomeText7 {
+ get {
+ return ResourceManager.GetString("NameWelcomeText7", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 If you want to contribute to i18n, you can star this project and create a Resource.resx resource file in your own language. 的本地化字符串。
+ ///
+ public static string NameWelcomeText8 {
+ get {
+ return ResourceManager.GetString("NameWelcomeText8", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 https://github.com/XTsat/BrowserPicker_i18n 的本地化字符串。
+ ///
+ public static string NameWelcomeText9 {
+ get {
+ return ResourceManager.GetString("NameWelcomeText9", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Backup 的本地化字符串。
+ ///
+ public static string TopNameBackup {
+ get {
+ return ResourceManager.GetString("TopNameBackup", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Browsers 的本地化字符串。
+ ///
+ public static string TopNameBrowsers {
+ get {
+ return ResourceManager.GetString("TopNameBrowsers", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Defaults 的本地化字符串。
+ ///
+ public static string TopNameDefaultsMatch {
+ get {
+ return ResourceManager.GetString("TopNameDefaultsMatch", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Done 的本地化字符串。
+ ///
+ public static string TopNameDone {
+ get {
+ return ResourceManager.GetString("TopNameDone", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Exit 的本地化字符串。
+ ///
+ public static string TopNameExit {
+ get {
+ return ResourceManager.GetString("TopNameExit", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Language 的本地化字符串。
+ ///
+ public static string TopNameLanguage {
+ get {
+ return ResourceManager.GetString("TopNameLanguage", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Behaviour 的本地化字符串。
+ ///
+ public static string TopNameSetting {
+ get {
+ return ResourceManager.GetString("TopNameSetting", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Test defaults 的本地化字符串。
+ ///
+ public static string TopNameTestDefaults {
+ get {
+ return ResourceManager.GetString("TopNameTestDefaults", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 URL shorteners 的本地化字符串。
+ ///
+ public static string TopNameUrlShorteners {
+ get {
+ return ResourceManager.GetString("TopNameUrlShorteners", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Welcome 的本地化字符串。
+ ///
+ public static string TopNameWelcome {
+ get {
+ return ResourceManager.GetString("TopNameWelcome", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Add 的本地化字符串。
+ ///
+ public static string UniAdd {
+ get {
+ return ResourceManager.GetString("UniAdd", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Cancel 的本地化字符串。
+ ///
+ public static string UniCancel {
+ get {
+ return ResourceManager.GetString("UniCancel", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Complete 的本地化字符串。
+ ///
+ public static string UniCompleted {
+ get {
+ return ResourceManager.GetString("UniCompleted", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Incorrect 的本地化字符串。
+ ///
+ public static string UniError {
+ get {
+ return ResourceManager.GetString("UniError", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Ok 的本地化字符串。
+ ///
+ public static string UniOk {
+ get {
+ return ResourceManager.GetString("UniOk", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Open 的本地化字符串。
+ ///
+ public static string UniOpen {
+ get {
+ return ResourceManager.GetString("UniOpen", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Remove 的本地化字符串。
+ ///
+ public static string UniRemove {
+ get {
+ return ResourceManager.GetString("UniRemove", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Restart 的本地化字符串。
+ ///
+ public static string UniRestart {
+ get {
+ return ResourceManager.GetString("UniRestart", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Prompts 的本地化字符串。
+ ///
+ public static string UniTip {
+ get {
+ return ResourceManager.GetString("UniTip", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Browser Picker 的本地化字符串。
+ ///
+ public static string WindowsTitle {
+ get {
+ return ResourceManager.GetString("WindowsTitle", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 An unhandled exception has occurred 的本地化字符串。
+ ///
+ public static string WindowsTitleCrashed {
+ get {
+ return ResourceManager.GetString("WindowsTitleCrashed", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Edit Browser 的本地化字符串。
+ ///
+ public static string WindowsTitleEdit {
+ get {
+ return ResourceManager.GetString("WindowsTitleEdit", resourceCulture);
+ }
+ }
+ }
+}
diff --git a/src/BrowserPicker.App/Resources/i18n.resx b/src/BrowserPicker.App/Resources/i18n.resx
new file mode 100644
index 0000000..18460ff
--- /dev/null
+++ b/src/BrowserPicker.App/Resources/i18n.resx
@@ -0,0 +1,460 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ Browser Picker
+
+
+ Browsers
+
+
+ Welcome
+
+
+ Behaviour
+
+
+ Defaults
+
+
+ Test defaults
+
+
+ Backup
+
+
+ URL shorteners
+
+
+ Welcome to BrowserPicker!
+ @MutedRule(PunctuationLead)@MutedRule(StringFormat)@MutedRule(WhiteSpaceLead)@MutedRule(WhiteSpaceTail)@MutedRule(PunctuationTail)
+
+
+ Here is the software settings window,
+ @MutedRule(PunctuationLead)@MutedRule(StringFormat)@MutedRule(WhiteSpaceLead)@MutedRule(WhiteSpaceTail)@MutedRule(PunctuationTail)
+
+
+ This software can select a customized browser when opening a link
+ @MutedRule(PunctuationLead)@MutedRule(StringFormat)@MutedRule(WhiteSpaceLead)@MutedRule(WhiteSpaceTail)@MutedRule(PunctuationTail)
+
+
+ If you think this software is helpful to you
+ @MutedRule(PunctuationLead)@MutedRule(StringFormat)@MutedRule(WhiteSpaceLead)@MutedRule(WhiteSpaceTail)@MutedRule(PunctuationTail)
+
+
+ Welcome to GitHub give a star☆
+ @MutedRule(PunctuationLead)@MutedRule(StringFormat)@MutedRule(WhiteSpaceLead)@MutedRule(WhiteSpaceTail)@MutedRule(PunctuationTail)
+
+
+ If you encounter software problems or have new ideas, please visit the project to submit an issue!
+ @MutedRule(PunctuationLead)@MutedRule(StringFormat)@MutedRule(WhiteSpaceLead)@MutedRule(WhiteSpaceTail)@MutedRule(PunctuationTail)
+
+
+ https://github.com/mortenn/BrowserPicker
+ @MutedRule(PunctuationLead)@MutedRule(StringFormat)@MutedRule(WhiteSpaceLead)@MutedRule(WhiteSpaceTail)@MutedRule(PunctuationTail)
+
+
+ If you want to contribute to i18n, you can star this project and create a Resource.resx resource file in your own language.
+ @MutedRule(PunctuationLead)@MutedRule(StringFormat)@MutedRule(WhiteSpaceLead)@MutedRule(WhiteSpaceTail)@MutedRule(PunctuationTail)
+
+
+ https://github.com/XTsat/BrowserPicker_i18n
+ @MutedRule(PunctuationLead)@MutedRule(StringFormat)@MutedRule(WhiteSpaceLead)@MutedRule(WhiteSpaceTail)@MutedRule(PunctuationTail)
+
+
+ This message will automatically disappear after 10 seconds
+ @MutedRule(PunctuationLead)@MutedRule(StringFormat)@MutedRule(WhiteSpaceLead)@MutedRule(WhiteSpaceTail)@MutedRule(PunctuationTail)
+
+
+ Enabled
+
+
+ Disabled
+
+
+ Edit
+
+
+ Edit browser definition
+
+
+ X
+
+
+ Remove browser from list
+
+
+ Add browser
+
+
+ Refresh browser list
+
+
+ Always show browser selection window
+
+
+ When no default is configured matching the url, use:
+
+
+ Use defaults even when browser is not running
+
+
+ Always ask when no default is matching url
+
+
+ Turn off transparency
+
+
+ Sort list manually
+
+
+ Sort list based on usage
+
+
+ Sort list alphabetically
+
+
+ Disallow network activity
+
+
+ URL resolution timeout:
+
+
+ Set default browser
+
+
+ Register the open method
+
+
+ Remove the open method
+
+
+ Set default browser per domain
+
+
+ Match by
+ @MutedRule(WhiteSpaceTail)
+
+
+ Pattern
+
+
+ Browser
+
+
+ Url:
+
+
+ Default browser matched:
+
+
+ Actual browser choice:
+
+
+ Back up or restore your BrowserPicker configuration:
+
+
+ Export BrowserPicker settings
+
+
+ Import BrowserPicker settings
+
+
+ These domains are known URL shorteners and gets probed on launch unless disallowed by settings
+
+
+ Default shorteners
+
+
+ Additional shorteners
+
+
+ ℹ️ Some additional jump pages not shown above
+
+
+ Some pages, such as outlook safelinks, have special handling where the target url is in the query string, these are not listed in this list, as they require additional information in code.
+
+
+ Add
+
+
+ Remove
+
+
+ Done
+
+
+ Exit
+
+
+ Name:
+
+
+ Command:
+
+
+ Command Args:
+
+
+ Browse file locations
+
+
+ Icon:
+
+
+ Expand file:// urls
+
+
+ Privacy Args:
+
+
+ Disable automatic updates
+
+
+ Hotkey:
+
+
+ (Automatic)
+
+
+ Ok
+
+
+ Cancel
+
+
+ Pins the window, making it not close on lost focus
+
+
+ Copy the URL to the clipboard
+
+
+ Edit the URL before opening it
+
+
+ Redirected URL
+
+
+ Configure browser list
+
+
+ Always ask
+
+
+ Remember my choice for
+
+
+ An unhandled exception has occurred
+
+
+ Unfortunately, BrowserPicker has crashed.
+
+
+ Please submit a bug report on https://github.com/mortenn/BrowserPicker.
+
+
+ Exception details:
+
+
+ Copy to clipboard
+
+
+ Scanning URL...
+
+
+ Edit Browser
+
+
+ Unable to get application filename
+
+
+ Incorrect
+
+
+ Unable to open link
+
+
+ Complete
+
+
+ Registration successful!
+1. Create a→ new desktop→ shortcut→ Enter the URL→ Right-click to→ open the method→ Select another app→ Find this program
+2. Right click any .html file→ Choose another app→ More apps→ Find this program
+3. If it is not displayed, it will take effect automatically after restarting the computer
+
+
+ Permission prompt
+
+
+ Normal user permission restricted: {0}
+Please turn off the security software and try again (no admin required)
+
+
+ Registration failed: {0}
+Please check if the app is blocked by security software
+
+
+ Are you sure you want to remove all open method associations?
+Once removed, it will not be possible to quickly open the webpage through this program.
+
+
+ Successfully removed all open method associations!
+
+
+ Remove failure:
+
+
+ Extension or ProgID cannot be empty
+
+
+ Open{0}file
+
+
+ Web link selector
+
+
+ Open with {0}
+
+
+ Open{0}link with{1}
+
+
+ Open
+
+
+ Restart
+
+
+ Do you want to restart Explorer for immediate effect?
+The desktop icon will disappear temporarily during the restart process and resume automatically after a few seconds.
+ Default no restart
+
+
+ Prompts
+
+
+ Failed to restart the explorer, it is recommended to restart the computer to take effect
+
+
+ Language
+
+
+ The application needs to restart to apply the new language settings.
+
+
\ No newline at end of file
diff --git a/src/BrowserPicker.App/Resources/i18n.zh-CN.resx b/src/BrowserPicker.App/Resources/i18n.zh-CN.resx
new file mode 100644
index 0000000..ee7cdf0
--- /dev/null
+++ b/src/BrowserPicker.App/Resources/i18n.zh-CN.resx
@@ -0,0 +1,448 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ 浏览器选择器
+
+
+ 浏览器
+
+
+ 欢迎
+
+
+ 设置
+
+
+ 浏览器匹配
+
+
+ 测试链接
+
+
+ 备份
+
+
+ URL 缩短器
+
+
+ 欢迎使用浏览器选择器!
+
+
+ 这里是软件的设置窗口,
+
+
+ 本软件可以在打开链接的时候选择自定义的浏览器
+
+
+ 如果你认为这个软件对你有帮助
+
+
+ 欢迎在 GitHub 上为汉化和原始仓库点个 star☆
+
+
+ 如果你对多语言有什么意见也可以反馈,
+
+
+ https://github.com/XTsat/BrowserPicker_i18n
+
+
+ 如果您遇到软件问题或有什么新想法,请访问原始项目提交问题!
+
+
+ https://github.com/mortenn/BrowserPicker
+
+
+ 这条消息将在 10 秒后自动消失
+
+
+ 启用
+
+
+ 禁用
+
+
+ 编辑
+
+
+ 编辑浏览器定义
+
+
+ X
+
+
+ 从列表中移除浏览器
+
+
+ 添加浏览器
+
+
+ 刷新浏览器列表
+
+
+ 始终显示浏览器选择窗口
+
+
+ 当无匹配链接的默认浏览器时,使用:
+
+
+ 即使浏览器未运行也使用默认设置
+
+
+ 无匹配默认浏览器时始终询问
+
+
+ 使用纯黑背景
+
+
+ 手动排序
+
+
+ 根据使用情况排序
+
+
+ 按字母顺序排序
+
+
+ 禁用链接解析
+
+
+ 链接解析超时时间(毫秒):
+
+
+ 设置默认浏览器
+
+
+ 注册打开方式
+
+
+ 移除打开方式
+
+
+ 按域名设置默认浏览器
+
+
+ 匹配
+
+
+ 模式
+
+
+ 浏览器
+
+
+ 链接:
+
+
+ 默认浏览器匹配:
+
+
+ 当前浏览器选择:
+
+
+ 备份或恢复您的浏览器选择器配置:
+
+
+ 导出设置
+
+
+ 导入设置
+
+
+ 这些域名是已知的网址缩短服务,除非通过设置禁止,否则会在启动时进行探测
+
+
+ 默认缩短工具
+
+
+ 其他缩短工具
+
+
+ ℹ️ 以上未显示一些额外的跳转页面
+
+
+ 有些页面(例如 Outlook 安全链接)有特殊的处理方式,目标 URL 位于查询字符串中,这些页面未列入此列表,因为它们需要在代码中提供额外的信息。
+
+
+ 添加
+
+
+ 移除
+
+
+ 关闭
+
+
+ 退出程序
+
+
+ 名称:
+
+
+ 文件目标:
+
+
+ 启动选项:
+
+
+ 浏览文件位置
+
+
+ 图标:
+
+
+ 展开 file:// 网址
+
+
+ 无痕参数:
+
+
+ 禁用自动更新(修改默认配置请打开)
+
+
+ 快捷键:
+
+
+ (自动)
+
+
+ 确认
+
+
+ 取消
+
+
+ 固定窗口,使其在失去焦点时不会关闭
+
+
+ 将 URL 复制到剪贴板
+
+
+ 在打开前编辑 URL
+
+
+ 重定向 URL
+
+
+ 配置浏览器列表
+
+
+ 总是询问
+
+
+ 记住我的选择
+
+
+ 发生了一个未处理的异常
+
+
+ 很抱歉,浏览器选择器崩溃了。
+
+
+ 请在 https://github.com/mortenn/BrowserPicker 提交错误报告。
+
+
+ 异常详情:
+
+
+ 复制到剪贴板
+
+
+ 扫描 URL...
+
+
+ 编辑浏览器
+
+
+ 无法获取应用程序文件名
+
+
+ 错误
+
+
+ 无法打开链接
+
+
+ 完成
+
+
+ 注册成功!
+1. 桌面→新建→快捷方式→输入网址→右键→打开方式→选择其他应用→找到本程序
+2. 任意.html文件→右键→打开方式→选择其他应用→更多应用→找到本程序
+3. 若未显示,重启电脑后自动生效
+
+
+ 权限提示
+
+
+ 普通用户权限受限:{0}
+请关闭安全软件后重试(无需管理员)
+
+
+ 注册失败:{0}
+请检查应用是否被安全软件拦截
+
+
+ 确定要移除所有打开方式关联吗?
+移除后将无法通过本程序快速打开网页。
+
+
+ 已成功移除所有打开方式关联!
+
+
+ 移除失败:
+
+
+ 扩展名或ProgID不能为空
+
+
+ 打开{0}文件
+
+
+ 网页链接选择器
+
+
+ 用{0}打开
+
+
+ 用{0}打开{1}链接
+
+
+ 打开
+
+
+ 重启
+
+
+ 是否重启资源管理器以立即生效?
+重启过程中桌面图标会暂时消失,几秒后自动恢复。
+
+
+ 提示
+
+
+ 重启资源管理器失败,建议重启电脑以生效
+
+
+ Language
+
+
+ 应用程序需要重启以应用新的语言设置。
+
+
\ No newline at end of file
diff --git a/src/BrowserPicker.App/View/BrowserEditor.xaml b/src/BrowserPicker.App/View/BrowserEditor.xaml
index 5f05eeb..4992f7b 100644
--- a/src/BrowserPicker.App/View/BrowserEditor.xaml
+++ b/src/BrowserPicker.App/View/BrowserEditor.xaml
@@ -1,97 +1,233 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
-
+
-
-
-
+
+
-
-
+
+
+
-
-
-
+
+
-
+
+
+
-
-
+
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/BrowserPicker.App/View/BrowserList.xaml b/src/BrowserPicker.App/View/BrowserList.xaml
index 8ee59f8..05df4a9 100644
--- a/src/BrowserPicker.App/View/BrowserList.xaml
+++ b/src/BrowserPicker.App/View/BrowserList.xaml
@@ -1,242 +1,330 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 📌
-
-
-
-
-
- 📋
-
-
-
-
-
- ✏
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ✔️
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 📌
+
+
+
+
+
+ 📋
+
+
+
+
+
+ ✏
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ✔️
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/BrowserPicker.App/View/BrowserList.xaml.cs b/src/BrowserPicker.App/View/BrowserList.xaml.cs
index 7a9066a..3363cfd 100644
--- a/src/BrowserPicker.App/View/BrowserList.xaml.cs
+++ b/src/BrowserPicker.App/View/BrowserList.xaml.cs
@@ -31,4 +31,9 @@ private void Editor_KeyUp(object sender, System.Windows.Input.KeyEventArgs e)
}
e.Handled = true;
}
+
+ private void CheckBox_Checked(object sender, RoutedEventArgs e)
+ {
+
+ }
}
diff --git a/src/BrowserPicker.App/View/Configuration.xaml b/src/BrowserPicker.App/View/Configuration.xaml
index 44e9c87..1dca819 100644
--- a/src/BrowserPicker.App/View/Configuration.xaml
+++ b/src/BrowserPicker.App/View/Configuration.xaml
@@ -1,471 +1,724 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Welcome to Browser Picker, hope you enjoy my little app!
- I've gone ahead and opened up the settings window,
- so you can have a look around before you get started :)
-
-
- Please give it a ⭐ on github, so you will get notified when a new release is made.
-
-
- Should you run into any kind of trouble or get an idea,
- please visit github.com/mortenn/BrowserPicker and file an issue!
-
- Be seeing you!
-
-
- This message will self destruct in 10 seconds
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 🔼
- 🔽
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 🔼
+ 🔽
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/BrowserPicker.App/View/Configuration.xaml.cs b/src/BrowserPicker.App/View/Configuration.xaml.cs
index a816cc1..1a80d91 100644
--- a/src/BrowserPicker.App/View/Configuration.xaml.cs
+++ b/src/BrowserPicker.App/View/Configuration.xaml.cs
@@ -1,17 +1,597 @@
-namespace BrowserPicker.View;
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Reflection;
+using System.Runtime.InteropServices;
+using System.Windows;
+using System.Windows.Documents;
+using System.Windows.Input;
+using Microsoft.Win32;
+
+namespace BrowserPicker.View;
-///
-/// Interaction logic for Configuration.xaml
-///
public partial class Configuration
{
+
+ private void OnMouseEnter(object sender, MouseEventArgs e)
+ {
+ var hyperlink = sender as Hyperlink;
+ if (hyperlink != null)
+ {
+ hyperlink.TextDecorations = TextDecorations.Underline;
+ }
+ }
+
+ private void OnMouseLeave(object sender, MouseEventArgs e)
+ {
+ var hyperlink = sender as Hyperlink;
+ if (hyperlink != null)
+ {
+ hyperlink.TextDecorations = null;
+ }
+ }
+
+ private void Hyperlink_RequestNavigate(object sender, System.Windows.Navigation.RequestNavigateEventArgs e)
+ {
+ try
+ {
+ string url = e.Uri.ToString();
+
+ // 相对 URL 自动补充
+ if (!e.Uri.IsAbsoluteUri)
+ {
+ if (url.StartsWith("/"))
+ {
+ url = "https://github.com" + url;
+ }
+ else
+ {
+ url = "https://" + url;
+ }
+ }
+ else if (!url.StartsWith("http://") && !url.StartsWith("https://"))
+ {
+ url = "https://" + url;
+ }
+
+ Process.Start(new ProcessStartInfo(url)
+ {
+ UseShellExecute = true
+ });
+ }
+ catch (Exception ex)
+ {
+ string msg = BrowserPicker.Resources.i18n.CsConfigHyperlinkError;
+ string error = BrowserPicker.Resources.i18n.UniError;
+ MessageBox.Show($"{msg} '{e.Uri}': {ex.Message}", error, MessageBoxButton.OK, MessageBoxImage.Error);
+ }
+ }
+
+
+
+ // 外壳刷新API
+ [DllImport("shell32.dll", SetLastError = true)]
+ private static extern void SHChangeNotify(uint wEventId, uint uFlags, IntPtr dwItem1, IntPtr dwItem2);
+
+ private const uint SHCNE_ASSOCCHANGED = 0x08000000;
+ private const uint SHCNF_IDLIST = 0x0000;
+ private const uint SHCNF_FLUSH = 0x1000;
+
+ private readonly string _appPath;
+ private readonly string _appExeName;
+ private const string AppName = "BrowserPicker";
+ private const string ProgIdHtml = $"{AppName}.html";
+ private const string ProgIdUrl = $"{AppName}.url";
+
+ // 仅操作普通用户可写的路径,避免权限问题
+ private readonly string[] _fileTypes = new[] { ".htm", ".html", ".url" };
+ private readonly string[] _protocols = new[] { "http", "https" };
+ private readonly string[] _protocolFileAssocs = new[] { "InternetShortcut", "URLFile" };
+
public Configuration()
{
InitializeComponent();
+ // 确保路径非空
+ string msg = BrowserPicker.Resources.i18n.CsConfigConfigurationNameErorr;
+ var entryAssembly = Assembly.GetEntryAssembly();
+ _appPath = !string.IsNullOrEmpty(Environment.ProcessPath)
+ ? Environment.ProcessPath
+ : Path.Combine(AppContext.BaseDirectory, Path.GetFileName(AppName + ".exe"));
+ _appExeName = Path.GetFileName(_appPath) ?? throw new InvalidOperationException(msg);
+ }
+
+ // 注册按钮点击事件
+ private void RegisterOpenWith_Click(object sender, RoutedEventArgs e)
+ {
+ try
+ {
+ // 1. 优先创建ProgID(系统核心识别项)
+ CreateProgId(ProgIdHtml, "HTML", isUrl: false);
+ CreateProgId(ProgIdUrl, "URL", isUrl: true);
+
+ // 2. 注册文件类型(仅User)
+ foreach (var ext in _fileTypes)
+ {
+ RegisterFileType(ext, ext == ".url" ? ProgIdUrl : ProgIdHtml);
+ }
+
+ // 3. 注册URL协议
+ foreach (var proto in _protocols)
+ {
+ RegisterUrlProtocol(proto, ProgIdUrl);
+ AddProtocolContextMenu(proto); // 直接加菜单,绕开权限限制
+ }
+
+ // 4. 注册为默认程序
+ RegisterAsDefaultProgram();
+
+ // 5. 关联系统URL文件类型
+ foreach (var fileType in _protocolFileAssocs)
+ {
+ RegisterProtocolFileAssoc(fileType);
+ }
+
+ // 6. 无感知刷新(不重启资源管理器)
+ RefreshSystemAssociations(false);
+
+
+ string msg = BrowserPicker.Resources.i18n.CsConfigRegistrationSuccessful;
+ string Completed = BrowserPicker.Resources.i18n.UniCompleted;
+
+ MessageBox.Show(msg, Completed, MessageBoxButton.OK, MessageBoxImage.Information);
+ }
+ catch (UnauthorizedAccessException ex)
+ {
+ string msg = BrowserPicker.Resources.i18n.CsConfigAdminTipText;
+ string tip = BrowserPicker.Resources.i18n.CsConfigAdminTip;
+ MessageBox.Show($"{msg}{ex.Message}", tip, MessageBoxButton.OK, MessageBoxImage.Warning);
+ }
+ catch (Exception ex)
+ {
+ string msg = BrowserPicker.Resources.i18n.CsConfigRegistrationFailed;
+ string error = BrowserPicker.Resources.i18n.UniError;
+ MessageBox.Show($"{msg}{ex.Message}", error, MessageBoxButton.OK, MessageBoxImage.Error);
+ }
+ }
+
+ // 移除按钮点击事件
+ private void UnregisterOpenWith_Click(object sender, RoutedEventArgs e)
+ {
+
+ string msg = BrowserPicker.Resources.i18n.CsConfigUnregisterTip;
+ string remove = BrowserPicker.Resources.i18n.UniRemove;
+ var result = MessageBox.Show(msg, remove, MessageBoxButton.YesNo, MessageBoxImage.Warning);
+
+ if (result != MessageBoxResult.Yes) return;
+
+ try
+ {
+ // 1. 删除文件类型关联
+ foreach (var ext in _fileTypes)
+ {
+ DeleteFileTypeAssoc(ext);
+ }
+
+ // 2. 删除URL协议关联
+ foreach (var proto in _protocols)
+ {
+ DeleteProtocolAssoc(proto);
+ DeleteProtocolContextMenu(proto);
+ }
+
+ // 3. 删除系统URL文件类型关联
+ foreach (var fileType in _protocolFileAssocs)
+ {
+ DeleteProtocolFileAssoc(fileType);
+ }
+
+ // 4. 删除默认程序注册
+ DeleteDefaultProgramReg();
+
+ // 5. 删除自定义ProgID
+ DeleteProgId(ProgIdHtml);
+ DeleteProgId(ProgIdUrl);
+
+ // 6. 无感知刷新
+ RefreshSystemAssociations(false);
+
+
+ string msg1 = BrowserPicker.Resources.i18n.CsConfigUnregisterSuccessful;
+ string Completed = BrowserPicker.Resources.i18n.UniCompleted;
+ MessageBox.Show(msg1, Completed, MessageBoxButton.OK, MessageBoxImage.Information);
+ }
+ catch (Exception ex)
+ {
+ string msg1 = BrowserPicker.Resources.i18n.CsConfigUnregisterFailed;
+ string error = BrowserPicker.Resources.i18n.UniError;
+ MessageBox.Show($"{msg1}{ex.Message}", error,
+ MessageBoxButton.OK, MessageBoxImage.Error);
+ }
+ }
+
+ // 文件类型注册
+ private void RegisterFileType(string extension, string progId)
+ {
+ string msg = BrowserPicker.Resources.i18n.CsConfigFileTypeRegistration;
+ if (string.IsNullOrEmpty(extension) || string.IsNullOrEmpty(progId))
+ throw new ArgumentNullException(msg);
+
+ // 仅操作 CurrentUser\Software\Classes\扩展名
+ using (var extKey = Registry.CurrentUser.CreateSubKey($@"Software\Classes\{extension}"))
+ {
+ string msg1 = BrowserPicker.Resources.i18n.CsConfigFileTypeRegistrationSuccessful;
+ extKey.SetValue("", progId, RegistryValueKind.String);
+ extKey.SetValue("FriendlyTypeName", $"{AppName} - {msg}{extension}", RegistryValueKind.String);
+
+ // OpenWithList(右键打开方式核心)
+ using (var openWithList = extKey.CreateSubKey(@"OpenWithList\" + AppName))
+ {
+ openWithList.SetValue("", _appPath, RegistryValueKind.String);
+ }
+
+ // OpenWithProgids(系统应用列表识别项)
+ using (var openWithProgids = extKey.CreateSubKey("OpenWithProgids"))
+ {
+ openWithProgids.SetValue(progId, string.Empty, RegistryValueKind.String);
+ }
+
+ // DefaultIcon(标记为有效应用,避免被过滤)
+ using (var iconKey = extKey.CreateSubKey("DefaultIcon"))
+ {
+ iconKey.SetValue("", $"\"{_appPath}\",0", RegistryValueKind.String);
+ }
+ }
+
+ // 操作 Explorer\FileExts(文件管理器关联)
+ using (var fileExtKey = Registry.CurrentUser.CreateSubKey(
+ $@"Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\{extension}"))
+ {
+ // OpenWithList(字母键关联)
+ using (var openWithKey = fileExtKey.CreateSubKey("OpenWithList"))
+ {
+ char keyChar = 'a';
+ while (openWithKey.GetValue(keyChar.ToString()) != null && keyChar <= 'z') keyChar++;
+ if (keyChar <= 'z')
+ {
+ openWithKey.SetValue(keyChar.ToString(), _appExeName, RegistryValueKind.String);
+ string mruList = openWithKey.GetValue("MRUList")?.ToString() ?? "";
+ openWithKey.SetValue("MRUList", keyChar + mruList.Trim(keyChar), RegistryValueKind.String);
+ }
+ }
+
+ // OpenWithProgids(补充关联)
+ using (var progIdsKey = fileExtKey.CreateSubKey("OpenWithProgids"))
+ {
+ progIdsKey.SetValue(progId, string.Empty, RegistryValueKind.String);
+ }
+ }
+ }
+
+ // 默认程序注册
+ private void RegisterAsDefaultProgram()
+ {
+ // 路径1:CurrentUser\Software\Classes\Applications\{EXE名}
+ using (var appKey = Registry.CurrentUser.CreateSubKey($@"Software\Classes\Applications\{_appExeName}"))
+ {
+ appKey.SetValue("FriendlyAppName", AppName, RegistryValueKind.String);
+ appKey.SetValue("ApplicationIcon", $"\"{_appPath}\",0", RegistryValueKind.String);
+ appKey.SetValue("NoOpenWith", string.Empty, RegistryValueKind.String); // 允许打开方式关联
+
+ // SupportedTypes(明确支持的类型)
+ using (var supportedTypesKey = appKey.CreateSubKey("SupportedTypes"))
+ {
+ supportedTypesKey.SetValue(".htm", string.Empty);
+ supportedTypesKey.SetValue(".html", string.Empty);
+ supportedTypesKey.SetValue(".url", string.Empty);
+ supportedTypesKey.SetValue("http", string.Empty);
+ supportedTypesKey.SetValue("https", string.Empty);
+ }
+
+ // 功能描述
+ using (var capabilitiesKey = appKey.CreateSubKey("Capabilities"))
+ {
+ string msg = BrowserPicker.Resources.i18n.CsConfigDefaultProgramRegistration;
+ capabilitiesKey.SetValue("ApplicationDescription", $"{AppName} - {msg}", RegistryValueKind.String);
+ capabilitiesKey.SetValue("ApplicationName", AppName, RegistryValueKind.String);
+
+ // 文件关联
+ using (var fileAssocKey = capabilitiesKey.CreateSubKey("FileAssociations"))
+ {
+ fileAssocKey.SetValue(".htm", ProgIdHtml, RegistryValueKind.String);
+ fileAssocKey.SetValue(".html", ProgIdHtml, RegistryValueKind.String);
+ fileAssocKey.SetValue(".url", ProgIdUrl, RegistryValueKind.String);
+ }
+
+ // URL协议关联
+ using (var urlAssocKey = capabilitiesKey.CreateSubKey("URLAssociations"))
+ {
+ urlAssocKey.SetValue("http", ProgIdUrl, RegistryValueKind.String);
+ urlAssocKey.SetValue("https", ProgIdUrl, RegistryValueKind.String);
+ }
+ }
+
+ // 打开命令
+ using (var shellKey = appKey.CreateSubKey(@"shell\open"))
+ {
+ string msg = BrowserPicker.Resources.i18n.CsConfigProgramRegistrationOpen;
+ shellKey.SetValue("FriendlyName", $"{msg} {AppName} ", RegistryValueKind.String);
+ shellKey.SetValue("Icon", $"\"{_appPath}\",0", RegistryValueKind.String);
+ using (var cmdKey = shellKey.CreateSubKey("command"))
+ {
+ cmdKey.SetValue("", $"\"{_appPath}\" \"%1\"", RegistryValueKind.String);
+ }
+ }
+ }
+
+ // 路径2:CurrentUser\Software\RegisteredApplications
+ using (var regAppsKey = Registry.CurrentUser.CreateSubKey(@"Software\RegisteredApplications"))
+ {
+ regAppsKey.SetValue(AppName, $@"Software\Classes\Applications\{_appExeName}\Capabilities", RegistryValueKind.String);
+ }
+ }
+
+ // URL协议注册(绕开权限限制)
+ private void RegisterUrlProtocol(string protocol, string progId)
+ {
+ if (string.IsNullOrEmpty(protocol) || string.IsNullOrEmpty(progId))
+ throw new ArgumentNullException("The protocol or ProgID cannot be empty");
+
+ using (var protoKey = Registry.CurrentUser.CreateSubKey($@"Software\Classes\{protocol}"))
+ {
+ protoKey.SetValue("", $"{AppName} - {protocol}Agreement", RegistryValueKind.String);
+ protoKey.SetValue("URL Protocol", string.Empty, RegistryValueKind.String);
+ protoKey.SetValue("EditFlags", 0x00000002, RegistryValueKind.DWord);
+ protoKey.SetValue("AlwaysShowExt", "yes", RegistryValueKind.String); // 强制显示
+
+ // OpenWithProgids(核心关联项)
+ using (var openWithProgids = protoKey.CreateSubKey("OpenWithProgids"))
+ {
+ openWithProgids.SetValue(progId, string.Empty, RegistryValueKind.String);
+ openWithProgids.SetValue(_appExeName, string.Empty, RegistryValueKind.String);
+ }
+
+ // 打开命令(优先级高)
+ using (var cmdKey = protoKey.CreateSubKey(@"shell\open\command"))
+ {
+ cmdKey.SetValue("", $"\"{_appPath}\" \"%1\"", RegistryValueKind.String);
+ cmdKey.SetValue(AppName, $"\"{_appPath}\" \"%1\"", RegistryValueKind.String);
+ }
+
+ // OpenWithList(补充关联)
+ using (var openWithList = protoKey.CreateSubKey(@"OpenWithList"))
+ {
+ openWithList.SetValue("", _appExeName, RegistryValueKind.String);
+ openWithList.SetValue(AppName, _appPath, RegistryValueKind.String);
+ }
+ }
+ }
+
+ // 直接添加右键菜单
+ private void AddProtocolContextMenu(string protocol)
+ {
+ string open = BrowserPicker.Resources.i18n.CsConfigProgramRegistrationOpen2;
+ // 直接添加协议右键菜单
+ using (var shellKey = Registry.CurrentUser.CreateSubKey($@"Software\Classes\{protocol}\shell\{AppName}"))
+ {
+ shellKey.SetValue("", $"{open} {AppName} {protocol.ToUpper()}", RegistryValueKind.String);
+ shellKey.SetValue("Position", "Top", RegistryValueKind.String); // 置顶
+ shellKey.SetValue("Icon", $"\"{_appPath}\",0", RegistryValueKind.String);
+ }
+
+ using (var cmdKey = Registry.CurrentUser.CreateSubKey($@"Software\Classes\{protocol}\shell\{AppName}\command"))
+ {
+ cmdKey.SetValue("", $"\"{_appPath}\" \"%1\"", RegistryValueKind.String);
+ }
+
+ // 添加URL文件类型右键菜单
+ foreach (var fileType in _protocolFileAssocs)
+ {
+ using (var shellKey = Registry.CurrentUser.CreateSubKey($@"Software\Classes\{fileType}\shell\{AppName}_{protocol}"))
+ {
+ shellKey.SetValue("", $"{open} {AppName} {protocol.ToUpper()}", RegistryValueKind.String);
+ }
+ using (var cmdKey = Registry.CurrentUser.CreateSubKey($@"Software\Classes\{fileType}\shell\{AppName}_{protocol}\command"))
+ {
+ cmdKey.SetValue("", $"\"{_appPath}\" \"%1\"", RegistryValueKind.String);
+ }
+ }
+ }
+
+ // 关联系统URL文件类型
+ private void RegisterProtocolFileAssoc(string fileType)
+ {
+ using (var fileTypeKey = Registry.CurrentUser.CreateSubKey($@"Software\Classes\{fileType}"))
+ {
+ // OpenWithList
+ using (var openWithList = fileTypeKey.CreateSubKey(@"OpenWithList\" + AppName))
+ {
+ openWithList.SetValue("", _appPath, RegistryValueKind.String);
+ }
+
+ // OpenWithProgids
+ using (var openWithProgids = fileTypeKey.CreateSubKey("OpenWithProgids"))
+ {
+ openWithProgids.SetValue(ProgIdUrl, string.Empty, RegistryValueKind.String);
+ }
+ }
+ }
+
+ // 创建ProgID(系统识别核心)
+ private void CreateProgId(string progId, string description, bool isUrl)
+ {
+ using (var progKey = Registry.CurrentUser.CreateSubKey($@"Software\Classes\{progId}"))
+ {
+ string open = BrowserPicker.Resources.i18n.UniOpen;
+ progKey.SetValue("", $"{AppName} - {description}", RegistryValueKind.String);
+ progKey.SetValue("FriendlyTypeName", $"{AppName} - {open}{description}", RegistryValueKind.String);
+ if (isUrl) progKey.SetValue("URL Protocol", string.Empty, RegistryValueKind.String);
+
+ // 打开命令
+ using (var cmdKey = progKey.CreateSubKey(@"shell\open\command"))
+ {
+ cmdKey.SetValue("", $"\"{_appPath}\" \"%1\"", RegistryValueKind.String);
+ }
+
+ // 默认图标
+ using (var iconKey = progKey.CreateSubKey("DefaultIcon"))
+ {
+ iconKey.SetValue("", $"\"{_appPath}\",0", RegistryValueKind.String);
+ }
+ }
+ }
+
+ // 所有删除方法保持普通用户兼容
+ private void DeleteProtocolContextMenu(string protocol)
+ {
+ var shellKeyPath = $@"Software\Classes\{protocol}\shell\{AppName}";
+ Registry.CurrentUser.DeleteSubKeyTree(shellKeyPath, throwOnMissingSubKey: false);
+
+ foreach (var fileType in _protocolFileAssocs)
+ {
+ var fileTypeShellPath = $@"Software\Classes\{fileType}\shell\{AppName}_{protocol}";
+ Registry.CurrentUser.DeleteSubKeyTree(fileTypeShellPath, throwOnMissingSubKey: false);
+ }
+ }
+
+ private void DeleteProtocolFileAssoc(string fileType)
+ {
+ var fileTypeKeyPath = $@"Software\Classes\{fileType}";
+ using (var fileTypeKey = Registry.CurrentUser.OpenSubKey(fileTypeKeyPath, writable: true))
+ {
+ if (fileTypeKey != null)
+ {
+ fileTypeKey.DeleteSubKeyTree(@"OpenWithList\" + AppName, throwOnMissingSubKey: false);
+ using (var openWithProgids = fileTypeKey.OpenSubKey("OpenWithProgids", writable: true))
+ {
+ openWithProgids?.DeleteValue(ProgIdUrl, throwOnMissingValue: false);
+ }
+ }
+ }
}
- private void CheckBox_Checked(object sender, System.Windows.RoutedEventArgs e)
+ private void DeleteFileTypeAssoc(string extension)
{
+ var extKeyPath = $@"Software\Classes\{extension}";
+ using (var extKey = Registry.CurrentUser.OpenSubKey(extKeyPath, writable: true))
+ {
+ if (extKey != null)
+ {
+ extKey.DeleteSubKeyTree(@"OpenWithList\" + AppName, throwOnMissingSubKey: false);
+ extKey.DeleteSubKeyTree("DefaultIcon", throwOnMissingSubKey: false);
+ extKey.DeleteValue(ProgIdHtml, throwOnMissingValue: false);
+ extKey.DeleteValue(ProgIdUrl, throwOnMissingValue: false);
+ extKey.DeleteValue("FriendlyTypeName", throwOnMissingValue: false);
+ }
+ }
+
+ var fileExtKeyPath = $@"Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\{extension}";
+ using (var fileExtKey = Registry.CurrentUser.OpenSubKey(fileExtKeyPath, writable: true))
+ {
+ if (fileExtKey != null)
+ {
+ using (var openWithKey = fileExtKey.OpenSubKey("OpenWithList", writable: true))
+ {
+ if (openWithKey != null)
+ {
+ foreach (var valueName in openWithKey.GetValueNames())
+ {
+ if (string.Equals(openWithKey.GetValue(valueName)?.ToString(), _appExeName, StringComparison.OrdinalIgnoreCase))
+ {
+ openWithKey.DeleteValue(valueName);
+ var mruList = openWithKey.GetValue("MRUList")?.ToString() ?? "";
+ if (mruList.Contains(valueName))
+ {
+ openWithKey.SetValue("MRUList", mruList.Replace(valueName, ""));
+ }
+ break;
+ }
+ }
+ }
+ }
+ using (var progIdsKey = fileExtKey.OpenSubKey("OpenWithProgids", writable: true))
+ {
+ progIdsKey?.DeleteValue(ProgIdHtml, throwOnMissingValue: false);
+ progIdsKey?.DeleteValue(ProgIdUrl, throwOnMissingValue: false);
+ }
+ }
+ }
+ }
+ private void DeleteProtocolAssoc(string protocol)
+ {
+ var protoKeyPath = $@"Software\Classes\{protocol}";
+ using (var protoKey = Registry.CurrentUser.OpenSubKey(protoKeyPath, writable: true))
+ {
+ if (protoKey != null)
+ {
+ protoKey.DeleteSubKeyTree(@"OpenWithList\" + AppName, throwOnMissingSubKey: false);
+ protoKey.DeleteSubKeyTree(@"shell\open\command", throwOnMissingSubKey: false);
+ protoKey.DeleteSubKeyTree("OpenWithProgids", throwOnMissingSubKey: false);
+ protoKey.DeleteValue(ProgIdUrl, throwOnMissingValue: false);
+ protoKey.DeleteValue("EditFlags", throwOnMissingValue: false);
+ protoKey.DeleteValue("AlwaysShowExt", throwOnMissingValue: false);
+ }
+ }
+ }
+
+ private void DeleteDefaultProgramReg()
+ {
+ var appKeyPath = $@"Software\Classes\Applications\{_appExeName}";
+ Registry.CurrentUser.DeleteSubKeyTree(appKeyPath, throwOnMissingSubKey: false);
+
+ using (var regAppsKey = Registry.CurrentUser.OpenSubKey(@"Software\RegisteredApplications", writable: true))
+ {
+ regAppsKey?.DeleteValue(AppName, throwOnMissingValue: false);
+ }
+ }
+
+ private void DeleteProgId(string progId)
+ {
+ var progIdPath = $@"Software\Classes\{progId}";
+ Registry.CurrentUser.DeleteSubKeyTree(progIdPath, throwOnMissingSubKey: false);
+ }
+
+ // 无感知刷新(不重启资源管理器)
+ private void RefreshSystemAssociations(bool forceRestart = false)
+ {
+ // 强制刷新缓存
+ SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST | SHCNF_FLUSH, IntPtr.Zero, IntPtr.Zero);
+
+ // 仅当用户主动选择时才重启(默认不重启)
+ if (forceRestart)
+ {
+ string restart = BrowserPicker.Resources.i18n.UniRestart;
+ string msg = BrowserPicker.Resources.i18n.CsConfigRestartExplorerTip;
+ var confirmResult = MessageBox.Show(msg, restart, MessageBoxButton.YesNo, MessageBoxImage.Question);
+
+ if (confirmResult == MessageBoxResult.Yes)
+ {
+ try
+ {
+ Process.Start(new ProcessStartInfo("cmd.exe", "/c taskkill /f /im explorer.exe && start explorer.exe")
+ {
+ CreateNoWindow = true,
+ WindowStyle = ProcessWindowStyle.Hidden
+ });
+ }
+ catch
+ {
+ string tip = BrowserPicker.Resources.i18n.UniTip;
+ string msg1 = BrowserPicker.Resources.i18n.CsConfigRestartExplorerFailTip;
+ MessageBox.Show(msg1, tip, MessageBoxButton.OK, MessageBoxImage.Warning);
+ }
+ }
+ }
+ }
+
+ private void CheckBox_Checked(object sender, System.Windows.RoutedEventArgs e) { }
+
+ private void OpenDefaultAppsSettings_Click(object sender, RoutedEventArgs e)
+ {
+ Process.Start(new ProcessStartInfo
+ {
+ FileName = "ms-settings:defaultapps",
+ UseShellExecute = true
+ });
}
}
diff --git a/src/BrowserPicker.App/View/ExceptionReport.xaml b/src/BrowserPicker.App/View/ExceptionReport.xaml
index 960716b..217e1a3 100644
--- a/src/BrowserPicker.App/View/ExceptionReport.xaml
+++ b/src/BrowserPicker.App/View/ExceptionReport.xaml
@@ -1,89 +1,113 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/BrowserPicker.App/View/ExceptionReport.xaml.cs b/src/BrowserPicker.App/View/ExceptionReport.xaml.cs
index 31421aa..0c559e8 100644
--- a/src/BrowserPicker.App/View/ExceptionReport.xaml.cs
+++ b/src/BrowserPicker.App/View/ExceptionReport.xaml.cs
@@ -9,4 +9,9 @@ public ExceptionReport()
{
InitializeComponent();
}
+
+ private void Button_Click(object sender, System.Windows.RoutedEventArgs e)
+ {
+
+ }
}
diff --git a/src/BrowserPicker.App/View/LoadingWindow.xaml b/src/BrowserPicker.App/View/LoadingWindow.xaml
index cafbee9..e9644fe 100644
--- a/src/BrowserPicker.App/View/LoadingWindow.xaml
+++ b/src/BrowserPicker.App/View/LoadingWindow.xaml
@@ -1,30 +1,51 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
- Scanning URL...
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/BrowserPicker.App/View/MainWindow.xaml b/src/BrowserPicker.App/View/MainWindow.xaml
index d0c4361..f0d8ac5 100644
--- a/src/BrowserPicker.App/View/MainWindow.xaml
+++ b/src/BrowserPicker.App/View/MainWindow.xaml
@@ -1,67 +1,81 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/BrowserPicker.App/View/MainWindow.xaml.cs b/src/BrowserPicker.App/View/MainWindow.xaml.cs
index 11006bc..c95ac03 100644
--- a/src/BrowserPicker.App/View/MainWindow.xaml.cs
+++ b/src/BrowserPicker.App/View/MainWindow.xaml.cs
@@ -112,4 +112,9 @@ private void DragWindow(object sender, MouseButtonEventArgs args)
{
DragMove();
}
+
+ private void BrowserList_Loaded(object sender, RoutedEventArgs e)
+ {
+
+ }
}
diff --git a/src/BrowserPicker.App/ViewModel/ApplicationViewModel.cs b/src/BrowserPicker.App/ViewModel/ApplicationViewModel.cs
index 2ad98b8..da86b53 100644
--- a/src/BrowserPicker.App/ViewModel/ApplicationViewModel.cs
+++ b/src/BrowserPicker.App/ViewModel/ApplicationViewModel.cs
@@ -10,6 +10,12 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
+using BrowserPicker.Resources;
+using System.Globalization;
+using System.Text.RegularExpressions;
+using BrowserPicker.View;
+
+
#if DEBUG
using JetBrains.Annotations;
#endif
@@ -22,8 +28,84 @@ namespace BrowserPicker.ViewModel;
///
public sealed class ApplicationViewModel : ModelBase
{
+
+ private DelegateCommand? changeLanguage;
+ public ICommand ChangeLanguage => changeLanguage ??= new DelegateCommand(OnChangeLanguage);
+
+ private void OnChangeLanguage()
+ {
+ var currentCulture = Thread.CurrentThread.CurrentUICulture;
+ var supportedCultures = new[]
+ {
+ new CultureInfo("en-US"),
+ new CultureInfo("zh-CN")
+ };
+
+ // 查找当前语言索引
+ var currentIndex = Array.IndexOf(supportedCultures,
+ supportedCultures.FirstOrDefault(c => c.Name.StartsWith(currentCulture.TwoLetterISOLanguageName)) ?? supportedCultures[0]);
+
+ // 计算下一个语言索引
+ var nextIndex = (currentIndex + 1) % supportedCultures.Length;
+ var nextCulture = supportedCultures[nextIndex];
+
+ // 保存语言设置到配置文件中
+ AppConfigHelper.WriteAppConfig("language", nextCulture.Name);
+
+ ApplyLanguageAndRestart(nextCulture);
+ }
+
+ private void ApplyLanguageAndRestart(CultureInfo culture)
+ {
+ Thread.CurrentThread.CurrentUICulture = culture;
+ Thread.CurrentThread.CurrentCulture = culture;
+
+ RestartApplication();
+ }
+
+ private void RestartApplication()
+ {
+ var msg = BrowserPicker.Resources.i18n.CsConfigLanguageChangeRestartMessage;
+ var restart = BrowserPicker.Resources.i18n.UniRestart;
+
+ var result = MessageBox.Show(
+ msg,
+ restart,
+ MessageBoxButton.OK,
+ MessageBoxImage.Information);
+
+ Application.Current.Shutdown();
+ }
+
+ public class AppConfigHelper
+ {
+ public static string ReadAppConfig(string key)
+ {
+ try
+ {
+ System.Configuration.Configuration config = System.Configuration.ConfigurationManager.OpenExeConfiguration(System.Configuration.ConfigurationUserLevel.None);
+ return config.AppSettings.Settings[key].Value;
+ }
+ catch(Exception)
+ {
+ throw;
+ }
+ }
+
+ public static void WriteAppConfig(string key, string value)
+ {
+ System.Configuration.Configuration config = System.Configuration.ConfigurationManager.OpenExeConfiguration(System.Configuration.ConfigurationUserLevel.None);
+ if(config.AppSettings.Settings[key] != null)
+ config.AppSettings.Settings[key].Value = value;
+ else
+ config.AppSettings.Settings.Add(key, value);
+ config.Save(System.Configuration.ConfigurationSaveMode.Modified);
+ System.Configuration.ConfigurationManager.RefreshSection("appSettings");
+ }
+ }
+
private static readonly ILogger Logger = App.Services.GetRequiredService>();
-
+
#if DEBUG
///
/// Default constructor used for WPF designer support.
@@ -171,7 +253,7 @@ public void Initialize()
.ToList();
Logger.LogAutomationMatchesFound(auto.Count);
-
+
return auto.Count <= 0
? null
: auto.OrderByDescending(o => o.matchLength).First().rule.Browser;
@@ -201,7 +283,7 @@ public void Initialize()
/// Closes the URL editor, saving any changes made to the targeted URL.
///
public ICommand EndEdit => new DelegateCommand(CloseURLEditor);
-
+
///
/// Gets the view model responsible for managing application configuration settings.
/// Provides access to user preferences and saved browser configurations.
@@ -259,7 +341,7 @@ public bool AltPressed
get => alt_pressed;
set => SetProperty(ref alt_pressed, value);
}
-
+
///
/// Pins the window, keeping it around while the user does something else.
///
@@ -274,7 +356,7 @@ public bool Pinned
get => pinned;
private set => SetProperty(ref pinned, value);
}
-
+
///
/// Event triggered to initiate application shutdown.
/// It is wired to the method to terminate the application.
diff --git a/src/BrowserPicker.App/ViewModel/ConfigurationViewModel.cs b/src/BrowserPicker.App/ViewModel/ConfigurationViewModel.cs
index bf20363..a38dd83 100644
--- a/src/BrowserPicker.App/ViewModel/ConfigurationViewModel.cs
+++ b/src/BrowserPicker.App/ViewModel/ConfigurationViewModel.cs
@@ -10,6 +10,7 @@
using Microsoft.Win32;
using System;
using System.Diagnostics;
+using BrowserPicker.Resources;
#if DEBUG
using System.Threading.Tasks;
@@ -18,6 +19,8 @@
namespace BrowserPicker.ViewModel;
+
+
///
/// Represents the view model for configuring browser behavior and default settings
/// in the BrowserPicker application.
@@ -500,7 +503,7 @@ public string TestDefaultsResult
{
get
{
- return ParentViewModel.GetBrowserToLaunchForUrl(test_defaults_url) ?? "User choice";
+ return ParentViewModel.GetBrowserToLaunchForUrl(test_defaults_url) ?? "用户选择";
}
}
@@ -508,7 +511,7 @@ public string TestActualResult
{
get
{
- return ParentViewModel.GetBrowserToLaunch(test_defaults_url)?.Model.Name ?? "User choice";
+ return ParentViewModel.GetBrowserToLaunch(test_defaults_url)?.Model.Name ?? "用户选择";
}
}
}
diff --git a/src/BrowserPicker.Windows/AppSettings.cs b/src/BrowserPicker.Windows/AppSettings.cs
index d9a1215..1af555d 100644
--- a/src/BrowserPicker.Windows/AppSettings.cs
+++ b/src/BrowserPicker.Windows/AppSettings.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
@@ -10,6 +10,7 @@
using BrowserPicker.Framework;
using Microsoft.Extensions.Logging;
using Microsoft.Win32;
+using BrowserPicker;
namespace BrowserPicker.Windows;
@@ -298,14 +299,16 @@ public async Task SaveAsync(string fileName)
var settings = new SerializableSettings(this);
try
{
- await Task.CompletedTask;
+ string msg = BrowserPicker.Resources.csi18n.CsAppSettingsExport;
+ await Task.CompletedTask;
await using var fileStream = File.Open(fileName, FileMode.Create, FileAccess.Write);
await JsonSerializer.SerializeAsync(fileStream, settings, JsonOptions);
- BackupLog += $"Exported configuration to {fileName}\n";
+ BackupLog += $"{msg} {fileName}\n";
}
catch(Exception e)
- {
- BackupLog += $"Unable to parse backup file: {e.Message}";
+ {
+ string msg = BrowserPicker.Resources.csi18n.CsAppSettingsBackupError;
+ BackupLog += $"{msg} {e.Message}";
}
}
@@ -320,12 +323,13 @@ public async Task LoadAsync(string fileName)
}
catch (Exception ex)
{
- BackupLog += $"Unable to parse backup file: {ex.Message}";
+ string msg1 = BrowserPicker.Resources.csi18n.CsAppSettingsBackupError;
+ BackupLog += $"{msg1} {ex.Message}";
return;
}
if (settings == null)
{
- BackupLog += "Unable to load backup";
+ BackupLog += BrowserPicker.Resources.csi18n.CsAppSettingsBackupLoadError;
return;
}
@@ -334,7 +338,8 @@ public async Task LoadAsync(string fileName)
UpdateDefaults(settings.Defaults);
UpdateKeybinds(settings.KeyBindings);
- BackupLog += $"Imported configuration from {fileName}\n";
+ string msg = BrowserPicker.Resources.csi18n.CsAppSettingsImport;
+ BackupLog += $"{msg} {fileName}\n";
}
private void UpdateSettings(SerializableSettings settings)
diff --git a/src/BrowserPicker.Windows/BrowserPicker.Windows.csproj b/src/BrowserPicker.Windows/BrowserPicker.Windows.csproj
index a5d636a..1f7ee73 100644
--- a/src/BrowserPicker.Windows/BrowserPicker.Windows.csproj
+++ b/src/BrowserPicker.Windows/BrowserPicker.Windows.csproj
@@ -1,6 +1,6 @@
- 2.0.0.3
+ 2.2.4.4
enable
diff --git a/src/BrowserPicker/BrowserPicker.csproj b/src/BrowserPicker/BrowserPicker.csproj
index 5ddd395..aeae9dd 100644
--- a/src/BrowserPicker/BrowserPicker.csproj
+++ b/src/BrowserPicker/BrowserPicker.csproj
@@ -7,4 +7,17 @@
+
+
+ csi18n.resx
+ True
+ True
+
+
+
+
+ csi18n.Designer.cs
+ PublicResXFileCodeGenerator
+
+
\ No newline at end of file
diff --git a/src/BrowserPicker/Resources/csi18n.Designer.cs b/src/BrowserPicker/Resources/csi18n.Designer.cs
new file mode 100644
index 0000000..53e6b37
--- /dev/null
+++ b/src/BrowserPicker/Resources/csi18n.Designer.cs
@@ -0,0 +1,99 @@
+//------------------------------------------------------------------------------
+//
+// 此代码由工具生成。
+// 运行时版本:4.0.30319.42000
+//
+// 对此文件的更改可能会导致不正确的行为,并且如果
+// 重新生成代码,这些更改将会丢失。
+//
+//------------------------------------------------------------------------------
+
+namespace BrowserPicker.Resources {
+ using System;
+
+
+ ///
+ /// 一个强类型的资源类,用于查找本地化的字符串等。
+ ///
+ // 此类是由 StronglyTypedResourceBuilder
+ // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。
+ // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen
+ // (以 /str 作为命令选项),或重新生成 VS 项目。
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "18.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ public class csi18n {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal csi18n() {
+ }
+
+ ///
+ /// 返回此类使用的缓存的 ResourceManager 实例。
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ public static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("BrowserPicker.Resources.csi18n", typeof(csi18n).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// 重写当前线程的 CurrentUICulture 属性,对
+ /// 使用此强类型资源类的所有资源查找执行重写。
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ public static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ ///
+ /// 查找类似 Unable to parse backup file: 的本地化字符串。
+ ///
+ public static string CsAppSettingsBackupError {
+ get {
+ return ResourceManager.GetString("CsAppSettingsBackupError", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Unable to load backup 的本地化字符串。
+ ///
+ public static string CsAppSettingsBackupLoadError {
+ get {
+ return ResourceManager.GetString("CsAppSettingsBackupLoadError", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Exported configuration to 的本地化字符串。
+ ///
+ public static string CsAppSettingsExport {
+ get {
+ return ResourceManager.GetString("CsAppSettingsExport", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Imported configuration from 的本地化字符串。
+ ///
+ public static string CsAppSettingsImport {
+ get {
+ return ResourceManager.GetString("CsAppSettingsImport", resourceCulture);
+ }
+ }
+ }
+}
diff --git a/src/BrowserPicker/Resources/csi18n.resx b/src/BrowserPicker/Resources/csi18n.resx
new file mode 100644
index 0000000..34595d2
--- /dev/null
+++ b/src/BrowserPicker/Resources/csi18n.resx
@@ -0,0 +1,132 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ Exported configuration to
+
+
+ Unable to parse backup file:
+
+
+ Unable to load backup
+
+
+ Imported configuration from
+
+
\ No newline at end of file
diff --git a/src/BrowserPicker/Resources/csi18n.zh-CN.resx b/src/BrowserPicker/Resources/csi18n.zh-CN.resx
new file mode 100644
index 0000000..7797f53
--- /dev/null
+++ b/src/BrowserPicker/Resources/csi18n.zh-CN.resx
@@ -0,0 +1,132 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ 配置导出至
+
+
+ 无法解析备份文件:
+
+
+ 导入配置自
+
+
+ 无法加载备份
+
+
\ No newline at end of file
diff --git a/src/BrowserPicker/WellKnownBrowsers.cs b/src/BrowserPicker/WellKnownBrowsers.cs
index 558a658..8e55653 100644
--- a/src/BrowserPicker/WellKnownBrowsers.cs
+++ b/src/BrowserPicker/WellKnownBrowsers.cs
@@ -79,7 +79,7 @@ public sealed class Chrome : IWellKnownBrowser
public string PrivacyArgs => "--incognito ";
- public string PrivacyMode => "Open incognito";
+ public string PrivacyMode => "无痕模式打开";
}
public sealed class ChromeDevEdition : IWellKnownBrowser
@@ -94,7 +94,7 @@ public sealed class ChromeDevEdition : IWellKnownBrowser
public string PrivacyArgs => "--incognito ";
- public string PrivacyMode => "Open incognito";
+ public string PrivacyMode => "无痕模式打开";
}
public sealed class MicrosoftEdge : IWellKnownBrowser
@@ -109,7 +109,7 @@ public sealed class MicrosoftEdge : IWellKnownBrowser
public string PrivacyArgs => "-inprivate ";
- public string PrivacyMode => "Open in private mode";
+ public string PrivacyMode => "无痕模式打开";
}
public sealed class Edge : IWellKnownBrowser
@@ -124,7 +124,7 @@ public sealed class Edge : IWellKnownBrowser
public string PrivacyArgs => "-private ";
- public string PrivacyMode => "Open in private mode";
+ public string PrivacyMode => "无痕模式打开";
}
public sealed class InternetExplorer : IWellKnownBrowser
@@ -139,7 +139,7 @@ public sealed class InternetExplorer : IWellKnownBrowser
public string PrivacyArgs => "-private ";
- public string PrivacyMode => "Open in private mode";
+ public string PrivacyMode => "无痕模式打开";
}
public sealed class OperaStable : IWellKnownBrowser
@@ -154,5 +154,5 @@ public sealed class OperaStable : IWellKnownBrowser
public string PrivacyArgs => "--private ";
- public string PrivacyMode => "Open in private mode";
+ public string PrivacyMode => "无痕模式打开";
}
\ No newline at end of file
diff --git a/src/Directory.Build.props b/src/Directory.Build.props
index f50ae7d..b60ce4c 100644
--- a/src/Directory.Build.props
+++ b/src/Directory.Build.props
@@ -1,10 +1,10 @@
net9.0-windows
- 2.0.0.4
+ 2.2.4.4
-beta1
Runsafe
- Copyright © 2017-2024
+ Copyright © 2017-2025
Debug;Release
x64
True
diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props
index b9593ac..174d25c 100644
--- a/src/Directory.Packages.props
+++ b/src/Directory.Packages.props
@@ -5,8 +5,8 @@
-
+
-
+
\ No newline at end of file