Ver código fonte

ref!: change locale code format from xx_YY to xx-YY

Sv443 6 meses atrás
pai
commit
1d085eb882

+ 17 - 17
assets/locales.json

@@ -1,73 +1,73 @@
 {
-  "de_DE": {
+  "de-DE": {
     "name": "Deutsch (Deutschland)",
     "nameEnglish": "German",
     "emoji": "🇩🇪",
     "userscriptDesc": "Konfigurierbare Layout- und Benutzererfahrungs-Verbesserungen für YouTube Music™ und YouTube™",
     "authors": ["Sv443"],
-    "altLocales": ["de", "de_AT", "de_CH"]
+    "altLocales": ["de", "de-AT", "de-CH"]
   },
-  "en_US": {
+  "en-US": {
     "name": "English (United States)",
     "nameEnglish": "English (US)",
     "emoji": "🇺🇸",
     "userscriptDesc": "Configurable layout and user experience improvements for YouTube Music™ and YouTube™",
     "authors": ["Sv443"],
-    "altLocales": ["en", "en_CA", "en_GB", "en_AU"]
+    "altLocales": ["en", "en-CA", "en-GB", "en-AU"]
   },
-  "en_UK": {
+  "en-UK": {
     "name": "English (United Kingdom)",
     "nameEnglish": "English (UK)",
     "emoji": "🇬🇧",
     "userscriptDesc": "Configurable layout and user experience improvements for YouTube Music™ and YouTube™",
     "authors": ["Sv443"]
   },
-  "es_ES": {
+  "es-ES": {
     "name": "Español (España)",
     "nameEnglish": "Spanish",
     "emoji": "🇪🇸",
     "userscriptDesc": "Mejoras de diseño y experiencia de usuario configurables para YouTube Music™ y YouTube™",
     "authors": ["Sv443"],
-    "altLocales": ["es", "es_MX"]
+    "altLocales": ["es", "es-MX"]
   },
-  "fr_FR": {
+  "fr-FR": {
     "name": "Français (France)",
     "nameEnglish": "French",
     "emoji": "🇫🇷",
     "userscriptDesc": "Améliorations de la mise en page et de l'expérience utilisateur configurables pour YouTube Music™ et YouTube™",
     "authors": ["Sv443"],
-    "altLocales": ["fr", "fr_CA", "fr_BE", "fr_CH", "fr_LU"]
+    "altLocales": ["fr", "fr-CA", "fr-BE", "fr-CH", "fr-LU"]
   },
-  "hi_IN": {
+  "hi-IN": {
     "name": "हिंदी (भारत)",
     "nameEnglish": "Hindi",
     "emoji": "🇮🇳",
     "userscriptDesc": "YouTube Music™ और YouTube™ के लिए कॉन्फ़िगर करने योग्य लेआउट और उपयोगकर्ता अनुभव में सुधार",
     "authors": ["Sv443"],
-    "altLocales": ["hi", "hi_NP"]
+    "altLocales": ["hi", "hi-NP"]
   },
-  "ja_JA": {
+  "ja-JA": {
     "name": "日本語 (日本)",
     "nameEnglish": "Japanese",
     "emoji": "🇯🇵",
     "userscriptDesc": "YouTube Music™ と YouTube™ の構成可能なレイアウトとユーザー エクスペリエンスの向上",
     "authors": ["Sv443"],
-    "altLocales": ["ja", "ja_JP"]
+    "altLocales": ["ja", "ja-JP"]
   },
-  "pt_BR": {
+  "pt-BR": {
     "name": "Português (Brasil)",
     "nameEnglish": "Portuguese",
     "emoji": "🇧🇷",
     "userscriptDesc": "Melhorias configuráveis no layout e na experiência do usuário para o YouTube Music™ e o YouTube™",
     "authors": ["Sv443"],
-    "altLocales": ["pt", "pt_PT"]
+    "altLocales": ["pt", "pt-PT"]
   },
-  "zh_CN": {
+  "zh-CN": {
     "name": "中文(简化,中国)",
     "nameEnglish": "Chinese (simpl.)",
     "emoji": "🇨🇳",
     "userscriptDesc": "YouTube Music™ 和 YouTube™ 的可配置布局和用户体验改进",
     "authors": ["Sv443"],
-    "altLocales": ["zh", "zh_TW", "zh_HK"]
+    "altLocales": ["zh", "zh-TW", "zh-HK"]
   }
 }

+ 10 - 10
assets/translations/README.md

@@ -16,15 +16,15 @@ To submit or edit a translation, please follow [this guide](../../contributing.m
 ### Translation progress:
 |   | Locale | Translated keys | Based on |
 | :----: | ------ | --------------- | :------: |
-|  | [`en_US`](./en_US.json) | `324` (default locale) |  |
-| ✅ | [`de_DE`](./de_DE.json) | `324/324` (100%) | ─ |
-|  | [`en_UK`](./en_UK.json) | `324/324` (100%) | `en_US` |
-| ✅ | [`es_ES`](./es_ES.json) | `324/324` (100%) | ─ |
-| ✅ | [`fr_FR`](./fr_FR.json) | `324/324` (100%) | ─ |
-| ✅ | [`hi_IN`](./hi_IN.json) | `324/324` (100%) | ─ |
-| ✅ | [`ja_JA`](./ja_JA.json) | `324/324` (100%) | ─ |
-| ✅ | [`pt_BR`](./pt_BR.json) | `324/324` (100%) | ─ |
-| ✅ | [`zh_CN`](./zh_CN.json) | `324/324` (100%) | ─ |
+|  | [`en-US`](./en-US.json) | `324` (default locale) |  |
+| ✅ | [`de-DE`](./de-DE.json) | `324/324` (100%) | ─ |
+|  | [`en-UK`](./en-UK.json) | `324/324` (100%) | `en-US` |
+| ✅ | [`es-ES`](./es-ES.json) | `324/324` (100%) | ─ |
+| ✅ | [`fr-FR`](./fr-FR.json) | `324/324` (100%) | ─ |
+| ✅ | [`hi-IN`](./hi-IN.json) | `324/324` (100%) | ─ |
+| ✅ | [`ja-JA`](./ja-JA.json) | `324/324` (100%) | ─ |
+| ✅ | [`pt-BR`](./pt-BR.json) | `324/324` (100%) | ─ |
+| ✅ | [`zh-CN`](./zh-CN.json) | `324/324` (100%) | ─ |
 
 <sub>
 ✅ - Fully translated
@@ -38,7 +38,7 @@ To submit or edit a translation, please follow [this guide](../../contributing.m
 
 <br>
 
-If a translation is based on another translation, that means the keys from the base translation file are automatically applied if they are missing. This is used for locales that are very similar to each other, such as `en_UK` and `en_US`  
+If a translation is based on another translation, that means the keys from the base translation file are automatically applied if they are missing. This is used for locales that are very similar to each other, such as `en-UK` and `en-US`  
 This means to figure out which keys are untranslated, you will need to manually check against the base translation file.
 
 <br>

+ 0 - 0
assets/translations/de_DE.json → assets/translations/de-DE.json


+ 1 - 1
assets/translations/en_UK.json → assets/translations/en-UK.json

@@ -1,5 +1,5 @@
 {
-  "base": "en_US",
+  "base": "en-US",
   "translations": {
     "feature_category_behavior": "Behaviour",
     "vote_ratio_blue_gray": "Blue and grey"

+ 0 - 0
assets/translations/en_US.json → assets/translations/en-US.json


+ 0 - 0
assets/translations/es_ES.json → assets/translations/es-ES.json


+ 0 - 0
assets/translations/fr_FR.json → assets/translations/fr-FR.json


+ 0 - 0
assets/translations/hi_IN.json → assets/translations/hi-IN.json


+ 0 - 0
assets/translations/ja_JA.json → assets/translations/ja-JA.json


+ 0 - 0
assets/translations/pt_BR.json → assets/translations/pt-BR.json


+ 0 - 0
assets/translations/zh_CN.json → assets/translations/zh-CN.json


+ 29 - 29
contributing.md

@@ -36,16 +36,16 @@ Before submitting a translation, please check on [this document](https://github.
 > **Please make sure you always select the `develop` branch when translating, as the `main` branch is only used for releases.**  
   
 To submit a translation, please follow these steps:
-1. Copy the contents of the default translation file [`assets/translations/en_US.json`](./assets/translations/en_US.json)
-2. Replace the `en_US` part of the file name with the language code and locale code of the language you want to translate to  
-  You can find lists of [ISO 639-1 alpha-2 language codes here](https://en.wikipedia.org/wiki/List_of_ISO_639_language_codes#Table) and [ISO 3166-1 alpha-2 country codes here.](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2#Officially_assigned_code_elements)  
-  The final locale code should be in the format `languageCode_localeCode` (e.g. `en_US`, `en_UK`, ...)
+1. Copy the contents of the default translation file [`assets/translations/en-US.json`](./assets/translations/en-US.json)
+2. Replace the `en-US` part of the file name with the language code and locale code of the language you want to translate to  
+  You can find [a list of these BCP 47 codes here.](https://www.techonthenet.com/js/language_tags.php)  
+  The final locale code should always be in the format `language-COUNTRY` (e.g. `en-US`, `en-UK`, ...)
 3. Translate the strings inside the file, while making sure not to change the keys on the left side of the colon and to preserve the placeholders with the format %n (where n is any number starting at 1).  
-  If you don't want to finish it in one go, please remove the extra keys before submitting the file. They can always be added back by running the command `pnpm run tr-format -p -o=language_LOCALE` (see [this section](#editing-an-existing-translation) for more info).
+  If you don't want to finish it in one go, please remove the extra keys before submitting the file. They can always be added back by running the command `pnpm run tr-format -p -o=language-COUNTRY` (see [this section](#editing-an-existing-translation) for more info).
 4. If you like, you may also create a translation for the [`README-summary.md`](./README-summary.md) file for display on the userscript distribution sites  
-  Please duplicate the file `README-summary.md` and call it `README-summary-languageCode_localeCode.md` and place it in the [`assets/translations/`](./assets/translations/) folder.
+  Please duplicate the file `README-summary.md` and call it `README-summary-xx-YY.md` and place it in the [`assets/translations/`](./assets/translations/) folder.
 5. If you want to submit a pull request with the translated file:
-    1. Duplicate the `en_US.json` file in the folder [`assets/translations/`](./assets/translations/) by keeping the format `languageCode_localeCode.json`
+    1. Duplicate the `en-US.json` file in the folder [`assets/translations/`](./assets/translations/) by keeping the format `languageCode_localeCode.json`
     2. Edit it to your translated version and keep the left side of the colon unchanged
     3. Create the mapping in `assets/locales.json` by copying the English one and editing it (please make sure it's alphabetically ordered)
     4. Add your name to the respective `authors` property in [`assets/locales.json`](./assets/locales.json)
@@ -63,11 +63,11 @@ To edit an existing translation, please follow these steps:
 1. Set up the project for local development by following [this section](#setting-up-the-project-for-local-development)  
   Make sure you have forked the repository and cloned your fork instead of cloning the original repository.  
 2. Find the file for the language you want to edit in the folder [`assets/translations/`](./assets/translations/)
-3. Run the command `pnpm run tr-format -p -o=language_LOCALE`, where `language_LOCALE` is the part of the file name before the `.json` extension  
-  This will prepare the file for translation by providing the missing keys once in English and once without any value and also formatting the file to have the same structure as the base file `en_US.json`
+3. Run the command `pnpm run tr-format -p -o=language-COUNTRY, where `language-COUNTRY` is the part of the file name before the `.json` extension  
+  This will prepare the file for translation by providing the missing keys once in English and once without any value and also formatting the file to have the same structure as the base file `en-US.json`
 4. Edit the strings inside the file, while making sure not to change the keys on the left side of the colon and to preserve the placeholders with the format %n (where n is any number starting at 1).
 5. Make sure there are no duplicate keys in the file
-6. Run the command `pnpm run tr-format -o=language_LOCALE` to make sure the file is formatted correctly
+6. Run the command `pnpm run tr-format -o=language-COUNTRY` to make sure the file is formatted correctly
 7. Test for syntax errors and update translation progress with the command `pnpm run tr-progress`
 8. Open the file [`assets/translations/README.md`](./assets/translations/README.md) to see if you're still missing any untranslated keys (you don't have to translate them all, but it would of course be nice)
 9. I highly encourage you to test your changes to see if the wording fits into the respective context by following [this section](#setting-up-the-project-for-local-development)
@@ -130,11 +130,11 @@ To edit an existing translation, please follow these steps:
   Checks all translation files for missing strings and updates the progress table in `assets/translations/README.md`  
   Will also be run automatically after every script build.
 - **`pnpm run tr-format <arguments>`**  
-  Reformats all translation files so they match that of the base file `en_US.json`  
+  Reformats all translation files so they match that of the base file `en-US.json`  
   This includes sorting keys and adding the same empty lines and indentation.
   Arguments:  
   - `--prep` or `-p` - Prepares the files for translation via GitHub Copilot by providing the missing key once in English and once without any value
-  - `--only="<value>"` or `-o="<value>"` - Only applies formatting to the files of the specified locales. Has to be a quoted, case-sensitive, comma separated list! (e.g. `-o="fr_FR,de_DE"` or `-o="pt_BR"`)
+  - `--only="<value>"` or `-o="<value>"` - Only applies formatting to the files of the specified locales. Has to be a quoted, case-sensitive, comma separated list! (e.g. `-o="fr-FR,de-DE"` or `-o="pt-BR"`)
   - `--include-based` or `-b` - Also includes files which have a base locale specified
 - **`pnpm run tr-prep`**  
   Shorthand for `pnpm run tr-format --prep` (see above)
@@ -211,8 +211,8 @@ The main files you will be working with are:
 - Adding a locale (language & regional dialect):
   1. Add the locale code and info about the locale to the file [`assets/locales.json`](./assets/locales.json) by following the format of the other entries.  
     Please make sure the alphabetical order is kept.  
-    You can find lists of [ISO 639-1 alpha-2 language codes here](https://en.wikipedia.org/wiki/List_of_ISO_639_language_codes#Table) and [ISO 3166-1 alpha-2 country codes here.](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2#Officially_assigned_code_elements)  
-    The final locale code should be in the format `languageCode_localeCode` (e.g. `en_US`, `en_UK`, ...)
+    You can find [a list of BCP 47 codes here.](https://www.techonthenet.com/js/language_tags.php)  
+    The final locale code should be in the format `language-COUNTRY` (e.g. `en-US`, `en-UK`, ...)
   2. Add a translation file for the locale by following the instructions in the [translations section](#adding-translations-for-a-new-language)
   3. Your locale will be immediately available in the userscript after the next build.
 - Creating a release:
@@ -480,8 +480,8 @@ The usage and example blocks on each are written in TypeScript but can be used i
 >     version: "4.2.0",                               // required
 >     iconUrl: "https://picsum.photos/128/128",       // required
 >     description: { // required
->       en_US: "This plugin does cool stuff",      // required
->       de_DE: "Dieses Plugin macht coole Sachen", // (all other locales are optional)
+>       "en-US": "This plugin does cool stuff",      // required
+>       "de-DE": "Dieses Plugin macht coole Sachen", // (all other locales are optional)
 >       // (see all supported locale codes in "assets/locales.json")
 >     },
 >     license: { // (optional)
@@ -949,7 +949,7 @@ The usage and example blocks on each are written in TypeScript but can be used i
 > <details><summary><b>Example <i>(click to expand)</i></b></summary>
 > 
 > ```ts
-> unsafeWindow.BYTM.setLocale(myToken, "en_UK");
+> unsafeWindow.BYTM.setLocale(myToken, "en-UK");
 > ```
 > </details>
 
@@ -967,11 +967,11 @@ The usage and example blocks on each are written in TypeScript but can be used i
 > <details><summary><b>Example <i>(click to expand)</i></b></summary>
 > 
 > ```ts
-> unsafeWindow.BYTM.getLocale(); // "en_US"
+> unsafeWindow.BYTM.getLocale(); // "en-US"
 > 
-> unsafeWindow.BYTM.setLocale("en_UK");
+> unsafeWindow.BYTM.setLocale("de-DE");
 > 
-> unsafeWindow.BYTM.getLocale(); // "en_UK"
+> unsafeWindow.BYTM.getLocale(); // "de-DE"
 > ```
 > </details>
 
@@ -1015,8 +1015,8 @@ The usage and example blocks on each are written in TypeScript but can be used i
 > <details><summary><b>Example <i>(click to expand)</i></b></summary>
 > 
 > ```ts
-> unsafeWindow.BYTM.hasKeyFor("en_UK", "lyrics_rate_limited"); // true
-> unsafeWindow.BYTM.hasKeyFor("en_UK", "some_key_that_doesnt_exist"); // false
+> unsafeWindow.BYTM.hasKeyFor("en-UK", "lyrics_rate_limited"); // true
+> unsafeWindow.BYTM.hasKeyFor("en-UK", "some_key_that_doesnt_exist"); // false
 > ```
 > </details>
 
@@ -1030,7 +1030,7 @@ The usage and example blocks on each are written in TypeScript but can be used i
 >   
 > Description:  
 > Returns the translation for the provided translation key and currently set locale.  
-> To see a list of translations, check the file [`assets/translations/en_US.json`](assets/translations/en_US.json)  
+> To see a list of translations, check the file [`assets/translations/en-US.json`](assets/translations/en-US.json)  
 >   
 > Arguments:  
 > - `translationKey` - The key of the translation to get.
@@ -1041,7 +1041,7 @@ The usage and example blocks on each are written in TypeScript but can be used i
 > ```ts
 > const customConfigMenuTitle = document.createElement("div");
 > customConfigMenuTitle.textContent = unsafeWindow.BYTM.t("config_menu_title", "My cool BYTM Plugin");
-> // translated text: "My cool BYTM Plugin - Configuration" (if locale is en_US or en_UK)
+> // translated text: "My cool BYTM Plugin - Configuration" (if locale is en-US or en-UK)
 > ```
 > </details>
 
@@ -1055,7 +1055,7 @@ The usage and example blocks on each are written in TypeScript but can be used i
 >   
 > Description:  
 > Returns the translation for the provided translation key, including pluralization identifier and currently set locale.  
-> To see a list of translations, check the file [`assets/translations/en_US.json`](assets/translations/en_US.json)  
+> To see a list of translations, check the file [`assets/translations/en-US.json`](assets/translations/en-US.json)  
 >   
 > The pluralization identifier is determined by the number of items in the second argument.  
 > It can be either "1" or "n" and will be appended to the translation key separated by a hyphen.  
@@ -1096,7 +1096,7 @@ The usage and example blocks on each are written in TypeScript but can be used i
 > Description:  
 > Returns the translation for the provided translation key and locale.  
 > Useful to get the translation for a specific locale without changing the currently set locale.  
-> To see a list of possible translation values, check the file [`assets/translations/en_US.json`](assets/translations/en_US.json)  
+> To see a list of possible translation values, check the file [`assets/translations/en-US.json`](assets/translations/en-US.json)  
 >   
 > Arguments:  
 > - `locale` - The locale to get the translation for.
@@ -1116,7 +1116,7 @@ The usage and example blocks on each are written in TypeScript but can be used i
 > Description:  
 > Returns the translation for the provided translation key, including pluralization identifier and locale.  
 > Useful to get the translation for a specific locale without changing the currently set locale.  
-> To see a list of possible translation values, check the file [`assets/translations/en_US.json`](assets/translations/en_US.json)  
+> To see a list of possible translation values, check the file [`assets/translations/en-US.json`](assets/translations/en-US.json)  
 >   
 > Arguments:  
 > - `locale` - The locale to get the translation for.
@@ -2042,11 +2042,11 @@ The usage and example blocks on each are written in TypeScript but can be used i
 > // (underscores in numbers are ignored in JS/TS)
 > const num = 123_456_789;
 > 
-> setLocale(myToken, "de_DE");             // German's commas and dots are swapped:
+> setLocale(myToken, "de-DE");             // German's commas and dots are swapped compared to English:
 > console.log(formatNumber(num, "short")); // 123,5 Mio.
 > console.log(formatNumber(num, "long"));  // 123.456.789
 > 
-> setLocale(myToken, "hi_HI");             // In Hindi it's a bit different:
+> setLocale(myToken, "hi-IN");             // In Hindi, the separators are sometimes every two digits:
 > console.log(formatNumber(num, "long"));  // 12,34,56,789
 > ```
 > </details>

+ 112 - 115
dist/BetterYTM.user.js

@@ -40,7 +40,7 @@
 // @license           AGPL-3.0-only
 // @author            Sv443
 // @copyright         Sv443 (https://github.com/Sv443)
-// @icon              https://raw.githubusercontent.com/Sv443/BetterYTM/d3718dab/assets/images/logo/logo_dev_48.png
+// @icon              https://raw.githubusercontent.com/Sv443/BetterYTM/007cd35e/assets/images/logo/logo_dev_48.png
 // @match             https://music.youtube.com/*
 // @match             https://www.youtube.com/*
 // @run-at            document-start
@@ -56,59 +56,59 @@
 // @grant             GM.openInTab
 // @grant             unsafeWindow
 // @noframes
-// @resource          css-above_queue_btns       https://raw.githubusercontent.com/Sv443/BetterYTM/d3718dab/assets/style/aboveQueueBtns.css
-// @resource          css-anchor_improvements    https://raw.githubusercontent.com/Sv443/BetterYTM/d3718dab/assets/style/anchorImprovements.css
-// @resource          css-auto_like              https://raw.githubusercontent.com/Sv443/BetterYTM/d3718dab/assets/style/autoLike.css
-// @resource          css-bundle                 https://raw.githubusercontent.com/Sv443/BetterYTM/d3718dab/dist/BetterYTM.css
-// @resource          css-fix_hdr                https://raw.githubusercontent.com/Sv443/BetterYTM/d3718dab/assets/style/fixHDR.css
-// @resource          css-fix_playerpage_theming https://raw.githubusercontent.com/Sv443/BetterYTM/d3718dab/assets/style/fixPlayerPageTheming.css
-// @resource          css-fix_spacing            https://raw.githubusercontent.com/Sv443/BetterYTM/d3718dab/assets/style/fixSpacing.css
-// @resource          css-fix_sponsorblock       https://raw.githubusercontent.com/Sv443/BetterYTM/d3718dab/assets/style/fixSponsorBlock.css
-// @resource          css-show_votes             https://raw.githubusercontent.com/Sv443/BetterYTM/d3718dab/assets/style/showVotes.css
-// @resource          css-vol_slider_size        https://raw.githubusercontent.com/Sv443/BetterYTM/d3718dab/assets/style/volSliderSize.css
-// @resource          doc-changelog              https://raw.githubusercontent.com/Sv443/BetterYTM/main/changelog.md?u=e97a0031-277f-42de-b2f0-10bfdcb1666e
-// @resource          font-cascadia_code_ttf     https://raw.githubusercontent.com/Sv443/BetterYTM/d3718dab/assets/fonts/CascadiaCode.ttf
-// @resource          font-cascadia_code_woff    https://raw.githubusercontent.com/Sv443/BetterYTM/d3718dab/assets/fonts/CascadiaCode.woff
-// @resource          font-cascadia_code_woff2   https://raw.githubusercontent.com/Sv443/BetterYTM/d3718dab/assets/fonts/CascadiaCode.woff2
-// @resource          icon-advanced_mode         https://raw.githubusercontent.com/Sv443/BetterYTM/d3718dab/assets/icons/plus_circle_small.svg
-// @resource          icon-alert                 https://raw.githubusercontent.com/Sv443/BetterYTM/d3718dab/assets/icons/alert.svg
-// @resource          icon-arrow_down            https://raw.githubusercontent.com/Sv443/BetterYTM/d3718dab/assets/icons/arrow_down.svg
-// @resource          icon-auto_like             https://raw.githubusercontent.com/Sv443/BetterYTM/d3718dab/assets/icons/auto_like.svg
-// @resource          icon-auto_like_enabled     https://raw.githubusercontent.com/Sv443/BetterYTM/d3718dab/assets/icons/auto_like_enabled.svg
-// @resource          icon-clear_list            https://raw.githubusercontent.com/Sv443/BetterYTM/d3718dab/assets/icons/clear_list.svg
-// @resource          icon-copy                  https://raw.githubusercontent.com/Sv443/BetterYTM/d3718dab/assets/icons/copy.svg
-// @resource          icon-delete                https://raw.githubusercontent.com/Sv443/BetterYTM/d3718dab/assets/icons/delete.svg
-// @resource          icon-edit                  https://raw.githubusercontent.com/Sv443/BetterYTM/d3718dab/assets/icons/edit.svg
-// @resource          icon-error                 https://raw.githubusercontent.com/Sv443/BetterYTM/d3718dab/assets/icons/error.svg
-// @resource          icon-experimental          https://raw.githubusercontent.com/Sv443/BetterYTM/d3718dab/assets/icons/beaker_small.svg
-// @resource          icon-globe                 https://raw.githubusercontent.com/Sv443/BetterYTM/d3718dab/assets/icons/globe.svg
-// @resource          icon-globe_small           https://raw.githubusercontent.com/Sv443/BetterYTM/d3718dab/assets/icons/globe_small.svg
-// @resource          icon-help                  https://raw.githubusercontent.com/Sv443/BetterYTM/d3718dab/assets/icons/help.svg
-// @resource          icon-image                 https://raw.githubusercontent.com/Sv443/BetterYTM/d3718dab/assets/icons/image.svg
-// @resource          icon-image_filled          https://raw.githubusercontent.com/Sv443/BetterYTM/d3718dab/assets/icons/image_filled.svg
-// @resource          icon-link                  https://raw.githubusercontent.com/Sv443/BetterYTM/d3718dab/assets/icons/link.svg
-// @resource          icon-lyrics                https://raw.githubusercontent.com/Sv443/BetterYTM/d3718dab/assets/icons/lyrics.svg
-// @resource          icon-prompt                https://raw.githubusercontent.com/Sv443/BetterYTM/d3718dab/assets/icons/help.svg
-// @resource          icon-reload                https://raw.githubusercontent.com/Sv443/BetterYTM/d3718dab/assets/icons/refresh.svg
-// @resource          icon-skip_to               https://raw.githubusercontent.com/Sv443/BetterYTM/d3718dab/assets/icons/skip_to.svg
-// @resource          icon-spinner               https://raw.githubusercontent.com/Sv443/BetterYTM/d3718dab/assets/icons/spinner.svg
-// @resource          icon-upload                https://raw.githubusercontent.com/Sv443/BetterYTM/d3718dab/assets/icons/upload.svg
-// @resource          img-close                  https://raw.githubusercontent.com/Sv443/BetterYTM/d3718dab/assets/images/close.png
-// @resource          img-discord                https://raw.githubusercontent.com/Sv443/BetterYTM/d3718dab/assets/images/external/discord.png
-// @resource          img-github                 https://raw.githubusercontent.com/Sv443/BetterYTM/d3718dab/assets/images/external/github.png
-// @resource          img-greasyfork             https://raw.githubusercontent.com/Sv443/BetterYTM/d3718dab/assets/images/external/greasyfork.png
-// @resource          img-logo                   https://raw.githubusercontent.com/Sv443/BetterYTM/d3718dab/assets/images/logo/logo_48.png
-// @resource          img-logo_dev               https://raw.githubusercontent.com/Sv443/BetterYTM/d3718dab/assets/images/logo/logo_dev_48.png
-// @resource          img-openuserjs             https://raw.githubusercontent.com/Sv443/BetterYTM/d3718dab/assets/images/external/openuserjs.png
-// @resource          trans-de_DE                https://raw.githubusercontent.com/Sv443/BetterYTM/d3718dab/assets/translations/de_DE.json
-// @resource          trans-en_UK                https://raw.githubusercontent.com/Sv443/BetterYTM/d3718dab/assets/translations/en_UK.json
-// @resource          trans-en_US                https://raw.githubusercontent.com/Sv443/BetterYTM/d3718dab/assets/translations/en_US.json
-// @resource          trans-es_ES                https://raw.githubusercontent.com/Sv443/BetterYTM/d3718dab/assets/translations/es_ES.json
-// @resource          trans-fr_FR                https://raw.githubusercontent.com/Sv443/BetterYTM/d3718dab/assets/translations/fr_FR.json
-// @resource          trans-hi_IN                https://raw.githubusercontent.com/Sv443/BetterYTM/d3718dab/assets/translations/hi_IN.json
-// @resource          trans-ja_JA                https://raw.githubusercontent.com/Sv443/BetterYTM/d3718dab/assets/translations/ja_JA.json
-// @resource          trans-pt_BR                https://raw.githubusercontent.com/Sv443/BetterYTM/d3718dab/assets/translations/pt_BR.json
-// @resource          trans-zh_CN                https://raw.githubusercontent.com/Sv443/BetterYTM/d3718dab/assets/translations/zh_CN.json
+// @resource          css-above_queue_btns       https://raw.githubusercontent.com/Sv443/BetterYTM/007cd35e/assets/style/aboveQueueBtns.css
+// @resource          css-anchor_improvements    https://raw.githubusercontent.com/Sv443/BetterYTM/007cd35e/assets/style/anchorImprovements.css
+// @resource          css-auto_like              https://raw.githubusercontent.com/Sv443/BetterYTM/007cd35e/assets/style/autoLike.css
+// @resource          css-bundle                 https://raw.githubusercontent.com/Sv443/BetterYTM/007cd35e/dist/BetterYTM.css
+// @resource          css-fix_hdr                https://raw.githubusercontent.com/Sv443/BetterYTM/007cd35e/assets/style/fixHDR.css
+// @resource          css-fix_playerpage_theming https://raw.githubusercontent.com/Sv443/BetterYTM/007cd35e/assets/style/fixPlayerPageTheming.css
+// @resource          css-fix_spacing            https://raw.githubusercontent.com/Sv443/BetterYTM/007cd35e/assets/style/fixSpacing.css
+// @resource          css-fix_sponsorblock       https://raw.githubusercontent.com/Sv443/BetterYTM/007cd35e/assets/style/fixSponsorBlock.css
+// @resource          css-show_votes             https://raw.githubusercontent.com/Sv443/BetterYTM/007cd35e/assets/style/showVotes.css
+// @resource          css-vol_slider_size        https://raw.githubusercontent.com/Sv443/BetterYTM/007cd35e/assets/style/volSliderSize.css
+// @resource          doc-changelog              https://raw.githubusercontent.com/Sv443/BetterYTM/main/changelog.md?u=b3bfa2e6-e3f1-4f81-ab57-de38bb6ad6c6
+// @resource          font-cascadia_code_ttf     https://raw.githubusercontent.com/Sv443/BetterYTM/007cd35e/assets/fonts/CascadiaCode.ttf
+// @resource          font-cascadia_code_woff    https://raw.githubusercontent.com/Sv443/BetterYTM/007cd35e/assets/fonts/CascadiaCode.woff
+// @resource          font-cascadia_code_woff2   https://raw.githubusercontent.com/Sv443/BetterYTM/007cd35e/assets/fonts/CascadiaCode.woff2
+// @resource          icon-advanced_mode         https://raw.githubusercontent.com/Sv443/BetterYTM/007cd35e/assets/icons/plus_circle_small.svg
+// @resource          icon-alert                 https://raw.githubusercontent.com/Sv443/BetterYTM/007cd35e/assets/icons/alert.svg
+// @resource          icon-arrow_down            https://raw.githubusercontent.com/Sv443/BetterYTM/007cd35e/assets/icons/arrow_down.svg
+// @resource          icon-auto_like             https://raw.githubusercontent.com/Sv443/BetterYTM/007cd35e/assets/icons/auto_like.svg
+// @resource          icon-auto_like_enabled     https://raw.githubusercontent.com/Sv443/BetterYTM/007cd35e/assets/icons/auto_like_enabled.svg
+// @resource          icon-clear_list            https://raw.githubusercontent.com/Sv443/BetterYTM/007cd35e/assets/icons/clear_list.svg
+// @resource          icon-copy                  https://raw.githubusercontent.com/Sv443/BetterYTM/007cd35e/assets/icons/copy.svg
+// @resource          icon-delete                https://raw.githubusercontent.com/Sv443/BetterYTM/007cd35e/assets/icons/delete.svg
+// @resource          icon-edit                  https://raw.githubusercontent.com/Sv443/BetterYTM/007cd35e/assets/icons/edit.svg
+// @resource          icon-error                 https://raw.githubusercontent.com/Sv443/BetterYTM/007cd35e/assets/icons/error.svg
+// @resource          icon-experimental          https://raw.githubusercontent.com/Sv443/BetterYTM/007cd35e/assets/icons/beaker_small.svg
+// @resource          icon-globe                 https://raw.githubusercontent.com/Sv443/BetterYTM/007cd35e/assets/icons/globe.svg
+// @resource          icon-globe_small           https://raw.githubusercontent.com/Sv443/BetterYTM/007cd35e/assets/icons/globe_small.svg
+// @resource          icon-help                  https://raw.githubusercontent.com/Sv443/BetterYTM/007cd35e/assets/icons/help.svg
+// @resource          icon-image                 https://raw.githubusercontent.com/Sv443/BetterYTM/007cd35e/assets/icons/image.svg
+// @resource          icon-image_filled          https://raw.githubusercontent.com/Sv443/BetterYTM/007cd35e/assets/icons/image_filled.svg
+// @resource          icon-link                  https://raw.githubusercontent.com/Sv443/BetterYTM/007cd35e/assets/icons/link.svg
+// @resource          icon-lyrics                https://raw.githubusercontent.com/Sv443/BetterYTM/007cd35e/assets/icons/lyrics.svg
+// @resource          icon-prompt                https://raw.githubusercontent.com/Sv443/BetterYTM/007cd35e/assets/icons/help.svg
+// @resource          icon-reload                https://raw.githubusercontent.com/Sv443/BetterYTM/007cd35e/assets/icons/refresh.svg
+// @resource          icon-skip_to               https://raw.githubusercontent.com/Sv443/BetterYTM/007cd35e/assets/icons/skip_to.svg
+// @resource          icon-spinner               https://raw.githubusercontent.com/Sv443/BetterYTM/007cd35e/assets/icons/spinner.svg
+// @resource          icon-upload                https://raw.githubusercontent.com/Sv443/BetterYTM/007cd35e/assets/icons/upload.svg
+// @resource          img-close                  https://raw.githubusercontent.com/Sv443/BetterYTM/007cd35e/assets/images/close.png
+// @resource          img-discord                https://raw.githubusercontent.com/Sv443/BetterYTM/007cd35e/assets/images/external/discord.png
+// @resource          img-github                 https://raw.githubusercontent.com/Sv443/BetterYTM/007cd35e/assets/images/external/github.png
+// @resource          img-greasyfork             https://raw.githubusercontent.com/Sv443/BetterYTM/007cd35e/assets/images/external/greasyfork.png
+// @resource          img-logo                   https://raw.githubusercontent.com/Sv443/BetterYTM/007cd35e/assets/images/logo/logo_48.png
+// @resource          img-logo_dev               https://raw.githubusercontent.com/Sv443/BetterYTM/007cd35e/assets/images/logo/logo_dev_48.png
+// @resource          img-openuserjs             https://raw.githubusercontent.com/Sv443/BetterYTM/007cd35e/assets/images/external/openuserjs.png
+// @resource          trans-de-DE                https://raw.githubusercontent.com/Sv443/BetterYTM/007cd35e/assets/translations/de-DE.json
+// @resource          trans-en-UK                https://raw.githubusercontent.com/Sv443/BetterYTM/007cd35e/assets/translations/en-UK.json
+// @resource          trans-en-US                https://raw.githubusercontent.com/Sv443/BetterYTM/007cd35e/assets/translations/en-US.json
+// @resource          trans-es-ES                https://raw.githubusercontent.com/Sv443/BetterYTM/007cd35e/assets/translations/es-ES.json
+// @resource          trans-fr-FR                https://raw.githubusercontent.com/Sv443/BetterYTM/007cd35e/assets/translations/fr-FR.json
+// @resource          trans-hi-IN                https://raw.githubusercontent.com/Sv443/BetterYTM/007cd35e/assets/translations/hi-IN.json
+// @resource          trans-ja-JA                https://raw.githubusercontent.com/Sv443/BetterYTM/007cd35e/assets/translations/ja-JA.json
+// @resource          trans-pt-BR                https://raw.githubusercontent.com/Sv443/BetterYTM/007cd35e/assets/translations/pt-BR.json
+// @resource          trans-zh-CN                https://raw.githubusercontent.com/Sv443/BetterYTM/007cd35e/assets/translations/zh-CN.json
 // @require           https://cdn.jsdelivr.net/npm/@sv443-network/[email protected]/dist/index.global.js
 // @require           https://cdn.jsdelivr.net/npm/[email protected]/lib/marked.umd.js
 // @require           https://cdn.jsdelivr.net/npm/[email protected]/lib/umd/index.js
@@ -164,7 +164,7 @@ var PluginIntent;
 const modeRaw = "development";
 const branchRaw = "develop";
 const hostRaw = "github";
-const buildNumberRaw = "d3718dab";
+const buildNumberRaw = "007cd35e";
 /** The mode in which the script was built (production or development) */
 const mode = (modeRaw.match(/^#{{.+}}$/) ? "production" : modeRaw);
 /** The branch to use in various URLs that point to the GitHub repo */
@@ -302,7 +302,7 @@ async function initTranslations(locale) {
         const transFile = await fetchLocaleJson(locale);
         let fallbackTrans = {};
         if (getFeature("localeFallback"))
-            fallbackTrans = await fetchLocaleJson("en_US");
+            fallbackTrans = await fetchLocaleJson("en-US");
         // merge with base translations if specified
         const baseTransFile = transFile.base ? await fetchLocaleJson(transFile.base) : undefined;
         const translations = Object.assign(Object.assign(Object.assign({}, ((_a = fallbackTrans === null || fallbackTrans === void 0 ? void 0 : fallbackTrans.translations) !== null && _a !== void 0 ? _a : {})), ((_b = baseTransFile === null || baseTransFile === void 0 ? void 0 : baseTransFile.translations) !== null && _b !== void 0 ? _b : {})), transFile.translations);
@@ -1270,7 +1270,8 @@ async function createLongBtn(_a) {
     btnElem.appendChild(txtElem);
     iconPosition === "right" && btnElem.appendChild(imgElem);
     return ripple ? createRipple(btnElem, { speed: "normal" }) : btnElem;
-}var de_DE = {
+}var langMapping = {
+	"de-DE": {
 	name: "Deutsch (Deutschland)",
 	nameEnglish: "German",
 	emoji: "🇩🇪",
@@ -1280,11 +1281,11 @@ async function createLongBtn(_a) {
 	],
 	altLocales: [
 		"de",
-		"de_AT",
-		"de_CH"
+		"de-AT",
+		"de-CH"
 	]
-};
-var en_US = {
+},
+	"en-US": {
 	name: "English (United States)",
 	nameEnglish: "English (US)",
 	emoji: "🇺🇸",
@@ -1294,12 +1295,12 @@ var en_US = {
 	],
 	altLocales: [
 		"en",
-		"en_CA",
-		"en_GB",
-		"en_AU"
+		"en-CA",
+		"en-GB",
+		"en-AU"
 	]
-};
-var en_UK = {
+},
+	"en-UK": {
 	name: "English (United Kingdom)",
 	nameEnglish: "English (UK)",
 	emoji: "🇬🇧",
@@ -1307,8 +1308,8 @@ var en_UK = {
 	authors: [
 		"Sv443"
 	]
-};
-var es_ES = {
+},
+	"es-ES": {
 	name: "Español (España)",
 	nameEnglish: "Spanish",
 	emoji: "🇪🇸",
@@ -1318,10 +1319,10 @@ var es_ES = {
 	],
 	altLocales: [
 		"es",
-		"es_MX"
+		"es-MX"
 	]
-};
-var fr_FR = {
+},
+	"fr-FR": {
 	name: "Français (France)",
 	nameEnglish: "French",
 	emoji: "🇫🇷",
@@ -1331,13 +1332,13 @@ var fr_FR = {
 	],
 	altLocales: [
 		"fr",
-		"fr_CA",
-		"fr_BE",
-		"fr_CH",
-		"fr_LU"
+		"fr-CA",
+		"fr-BE",
+		"fr-CH",
+		"fr-LU"
 	]
-};
-var hi_IN = {
+},
+	"hi-IN": {
 	name: "हिंदी (भारत)",
 	nameEnglish: "Hindi",
 	emoji: "🇮🇳",
@@ -1347,10 +1348,10 @@ var hi_IN = {
 	],
 	altLocales: [
 		"hi",
-		"hi_NP"
+		"hi-NP"
 	]
-};
-var ja_JA = {
+},
+	"ja-JA": {
 	name: "日本語 (日本)",
 	nameEnglish: "Japanese",
 	emoji: "🇯🇵",
@@ -1360,10 +1361,10 @@ var ja_JA = {
 	],
 	altLocales: [
 		"ja",
-		"ja_JP"
+		"ja-JP"
 	]
-};
-var pt_BR = {
+},
+	"pt-BR": {
 	name: "Português (Brasil)",
 	nameEnglish: "Portuguese",
 	emoji: "🇧🇷",
@@ -1373,10 +1374,10 @@ var pt_BR = {
 	],
 	altLocales: [
 		"pt",
-		"pt_PT"
+		"pt-PT"
 	]
-};
-var zh_CN = {
+},
+	"zh-CN": {
 	name: "中文(简化,中国)",
 	nameEnglish: "Chinese (simpl.)",
 	emoji: "🇨🇳",
@@ -1386,20 +1387,10 @@ var zh_CN = {
 	],
 	altLocales: [
 		"zh",
-		"zh_TW",
-		"zh_HK"
+		"zh-TW",
+		"zh-HK"
 	]
-};
-var langMapping = {
-	de_DE: de_DE,
-	en_US: en_US,
-	en_UK: en_UK,
-	es_ES: es_ES,
-	fr_FR: fr_FR,
-	hi_IN: hi_IN,
-	ja_JA: ja_JA,
-	pt_BR: pt_BR,
-	zh_CN: zh_CN
+}
 };//#region misc
 let domain;
 /**
@@ -1593,29 +1584,28 @@ async function getResourceUrl(name) {
 }
 /**
  * Returns the preferred locale of the user, provided it is supported by the userscript.
- * Prioritizes `navigator.language`, then `navigator.languages`, then `"en_US"` as a fallback.
+ * Prioritizes `navigator.language`, then `navigator.languages`, then `"en-US"` as a fallback.
  */
 function getPreferredLocale() {
     var _a;
-    const navLang = navigator.language.replace(/-/g, "_");
-    const navLangs = navigator.languages
-        .filter(lang => lang.match(/^[a-z]{2}(-|_)[A-Z]$/) !== null)
-        .map(lang => lang.replace(/-/g, "_"));
-    if (Object.entries(langMapping).find(([key]) => key === navLang))
-        return navLang;
-    for (const loc of navLangs) {
+    const nvLang = navigator.language;
+    const nvLangs = navigator.languages
+        .filter(lang => lang.match(/^[a-z]{2}(-|_)[A-Z]$/) !== null);
+    if (Object.entries(langMapping).find(([key]) => key === nvLang))
+        return nvLang;
+    for (const loc of nvLangs) {
         if (Object.entries(langMapping).find(([key]) => key === loc))
             return loc;
     }
-    // if navigator.languages has entries that aren't locale codes in the format xx_XX
+    // if navigator.languages has entries that aren't locale codes in the format xx-XX
     if (navigator.languages.some(lang => lang.match(/^[a-z]{2}$/))) {
-        for (const lang of navLangs) {
+        for (const lang of nvLangs) {
             const foundLoc = (_a = Object.entries(langMapping).find(([key]) => key.startsWith(lang))) === null || _a === void 0 ? void 0 : _a[0];
             if (foundLoc)
                 return foundLoc;
         }
     }
-    return "en_US";
+    return "en-US";
 }
 /** Returns the content behind the passed resource identifier as a string, for example to be assigned to an element's innerHTML property */
 async function resourceAsString(resource) {
@@ -2598,7 +2588,7 @@ async function renderBody$2() {
         const descEl = document.createElement("p");
         descEl.classList.add("bytm-plugin-list-row-desc");
         descEl.tabIndex = 0;
-        descEl.textContent = descEl.title = descEl.ariaLabel = (_a = plugin.description[getLocale()]) !== null && _a !== void 0 ? _a : plugin.description.en_US;
+        descEl.textContent = descEl.title = descEl.ariaLabel = (_a = plugin.description[getLocale()]) !== null && _a !== void 0 ? _a : plugin.description["en-US"];
         leftEl.appendChild(descEl);
         const linksList = document.createElement("div");
         linksList.classList.add("bytm-plugin-list-row-links-list");
@@ -6317,7 +6307,7 @@ const featInfo = {
         category: "lyrics",
         async click() {
             const entries = getLyricsCache().length;
-            const formattedEntries = entries.toLocaleString(getLocale().replace(/_/g, "-"), { style: "decimal", maximumFractionDigits: 0 });
+            const formattedEntries = entries.toLocaleString(getLocale(), { style: "decimal", maximumFractionDigits: 0 });
             if (await showPrompt({ type: "confirm", message: tp("lyrics_clear_cache_confirm_prompt", entries, formattedEntries) })) {
                 await clearLyricsCache();
                 await showPrompt({ type: "alert", message: t("lyrics_clear_cache_success") });
@@ -6471,7 +6461,7 @@ const featInfo = {
 };
 function noop() {
 }/** If this number is incremented, the features object data will be migrated to the new format */
-const formatVersion = 8;
+const formatVersion = 9;
 const defaultData = Object.keys(featInfo)
     // @ts-ignore
     .filter((ftKey) => { var _a; return ((_a = featInfo === null || featInfo === void 0 ? void 0 : featInfo[ftKey]) === null || _a === void 0 ? void 0 : _a.default) !== undefined; })
@@ -6565,6 +6555,13 @@ const migrations = {
             "autoLikeChannels"
         ]);
     },
+    // 8 -> 9 (v2.2)
+    9: (oldData) => {
+        oldData.locale = oldData.locale.replace("_", "-");
+        return useDefaultConfig(oldData, [
+            "autoLikePlayerBarToggleBtn",
+        ]);
+    },
 };
 /** Uses the default config as the base, then overwrites all values with the passed {@linkcode baseData}, then sets all passed {@linkcode resetKeys} to their default values */
 function useDefaultConfig(baseData, resetKeys) {
@@ -7513,8 +7510,8 @@ async function init() {
         const features = await initConfig();
         setLogLevel(features.logLevel);
         await initLyricsCache();
-        await initTranslations((_a = features.locale) !== null && _a !== void 0 ? _a : "en_US");
-        setLocale((_b = features.locale) !== null && _b !== void 0 ? _b : "en_US");
+        await initTranslations((_a = features.locale) !== null && _a !== void 0 ? _a : "en-US");
+        setLocale((_b = features.locale) !== null && _b !== void 0 ? _b : "en-US");
         try {
             initPlugins();
         }

+ 9 - 2
src/config.ts

@@ -1,6 +1,6 @@
 import { DataStore, compress, type DataMigrationsDict, decompress, type LooseUnion, clamp } from "@sv443-network/userutils";
 import { disableBeforeUnload, featInfo } from "./features/index.js";
-import { compressionSupported, error, getVideoTime, info, log, t } from "./utils/index.js";
+import { compressionSupported, error, getVideoTime, info, log, t, type TrLocale } from "./utils/index.js";
 import { emitSiteEvent } from "./siteEvents.js";
 import { compressionFormat } from "./constants.js";
 import { emitInterface } from "./interface.js";
@@ -9,7 +9,7 @@ import type { FeatureConfig, FeatureKey, NumberLength } from "./types.js";
 import { showPrompt } from "./dialogs/prompt.js";
 
 /** If this number is incremented, the features object data will be migrated to the new format */
-export const formatVersion = 8;
+export const formatVersion = 9;
 
 export const defaultData = (Object.keys(featInfo) as (keyof typeof featInfo)[])
   // @ts-ignore
@@ -118,6 +118,13 @@ export const migrations: DataMigrationsDict = {
       "autoLikeChannels"
     ]);
   },
+  // 8 -> 9 (v2.2)
+  9: (oldData: FeatureConfig) => {
+    oldData.locale = oldData.locale.replace("_", "-") as TrLocale;
+    return useDefaultConfig(oldData, [
+      "autoLikePlayerBarToggleBtn",
+    ]);
+  },
 } as const satisfies DataMigrationsDict;
 
 /** Uses the default config as the base, then overwrites all values with the passed {@linkcode baseData}, then sets all passed {@linkcode resetKeys} to their default values */

+ 1 - 1
src/dialogs/pluginList.ts

@@ -96,7 +96,7 @@ async function renderBody() {
     const descEl = document.createElement("p");
     descEl.classList.add("bytm-plugin-list-row-desc");
     descEl.tabIndex = 0;
-    descEl.textContent = descEl.title = descEl.ariaLabel = plugin.description[getLocale()] ?? plugin.description.en_US;
+    descEl.textContent = descEl.title = descEl.ariaLabel = plugin.description[getLocale()] ?? plugin.description["en-US"];
     leftEl.appendChild(descEl);
 
     const linksList = document.createElement("div");

+ 1 - 1
src/features/index.ts

@@ -624,7 +624,7 @@ export const featInfo = {
     category: "lyrics",
     async click() {
       const entries = getLyricsCache().length;
-      const formattedEntries = entries.toLocaleString(getLocale().replace(/_/g, "-"), { style: "decimal", maximumFractionDigits: 0 });
+      const formattedEntries = entries.toLocaleString(getLocale(), { style: "decimal", maximumFractionDigits: 0 });
       if(await showPrompt({ type: "confirm", message: tp("lyrics_clear_cache_confirm_prompt", entries, formattedEntries) })) {
         await clearLyricsCache();
         await showPrompt({ type: "alert", message: t("lyrics_clear_cache_success") });

+ 2 - 2
src/index.ts

@@ -106,8 +106,8 @@ async function init() {
 
     await initLyricsCache();
 
-    await initTranslations(features.locale ?? "en_US");
-    setLocale(features.locale ?? "en_US");
+    await initTranslations(features.locale ?? "en-US");
+    setLocale(features.locale ?? "en-US");
 
     try {
       initPlugins();

+ 1 - 2
src/tools/post-build.ts

@@ -331,7 +331,7 @@ function getLocalizedDescriptions() {
   try {
     const descriptions: string[] = [];
     for(const [locale, { userscriptDesc, ...rest }] of Object.entries(locales)) {
-      let loc = locale.replace(/_/, "-");
+      let loc = locale;
       if(loc.length < 5)
         loc += " ".repeat(5 - loc.length);
       descriptions.push(`// @description:${loc} ${userscriptDesc}`);
@@ -362,7 +362,6 @@ function getResourceUrl(path: string, ghRef: string, useTagInProd = true) {
   let assetPath = "/assets/";
   if(path.startsWith("/"))
     assetPath = "";
-  console.log("getResourceUrl", assetSource, path, ghRef);
   return assetSource === "local"
     ? `http://localhost:${devServerPort}${assetPath}${path}?b=${buildUuid}`
     : `https://raw.githubusercontent.com/${repo}/${mode === "development" || !useTagInProd ? ghRef : `v${pkg.version}`}${assetPath}${path}`;

+ 7 - 7
src/tools/tr-format.ts

@@ -12,11 +12,11 @@ const includeBased = Boolean(process.argv.find((v) => v.match(/--include-based/)
 
 async function run() {
   console.log("\nReformatting translation files...");
-  const en_US = await readFile("./assets/translations/en_US.json", "utf-8");
-  const en_US_obj = JSON.parse(en_US);
+  const enUS = await readFile("./assets/translations/en-US.json", "utf-8");
+  const enUS_obj = JSON.parse(enUS);
 
   const localeKeysRaw = Object.keys(locales) as TrLocale[];
-  const localeKeys = localeKeysRaw.filter((key) => key !== "en_US") as Exclude<TrLocale, "en_US">[];
+  const localeKeys = localeKeysRaw.filter((key) => key !== "en-US") as Exclude<TrLocale, "en-US">[];
 
   let reformattedAmt = 0;
 
@@ -24,21 +24,21 @@ async function run() {
     if(onlyLocales && !onlyLocales.includes(locale))
       continue;
 
-    // use en_US as base, replace values with values from locale file
+    // use en-US as base, replace values with values from locale file
 
-    let localeFile = en_US;
+    let localeFile = enUS;
     const localeObj = JSON.parse(await readFile(`./assets/translations/${locale}.json`, "utf-8"));
 
     if(!includeBased && localeObj.base)
       continue;
 
-    for(const k of Object.keys(en_US_obj.translations)) {
+    for(const k of Object.keys(enUS_obj.translations)) {
       const val = localeObj?.translations?.[k];
       if(val)
         localeFile = localeFile.replace(new RegExp(`"${k}":\\s+".*"`, "m"), `"${k}": "${escapeJsonVal(val).trim()}"`);
       else {
         if(prepTranslate)
-          localeFile = localeFile.replace(new RegExp(`\\n\\s+"${k}":\\s+".*",?`, "m"), `\n    "${k}": "",\n    "${k}": "${escapeJsonVal(en_US_obj.translations[k]).trim()}",`);
+          localeFile = localeFile.replace(new RegExp(`\\n\\s+"${k}":\\s+".*",?`, "m"), `\n    "${k}": "",\n    "${k}": "${escapeJsonVal(enUS_obj.translations[k]).trim()}",`);
         else
           localeFile = localeFile.replace(new RegExp(`\\n\\s+"${k}":\\s+".*",?`, "m"), "");
       }

+ 1 - 1
src/tools/tr-progress-template.md

@@ -18,7 +18,7 @@ To submit or edit a translation, please follow [this guide](../../contributing.m
 
 <br>
 
-If a translation is based on another translation, that means the keys from the base translation file are automatically applied if they are missing. This is used for locales that are very similar to each other, such as `en_UK` and `en_US`  
+If a translation is based on another translation, that means the keys from the base translation file are automatically applied if they are missing. This is used for locales that are very similar to each other, such as `en-UK` and `en-US`  
 This means to figure out which keys are untranslated, you will need to manually check against the base translation file.
 
 <br>

+ 9 - 9
src/tools/tr-progress.ts

@@ -37,15 +37,15 @@ async function run() {
   const trs = Object.keys(translations);
   console.log(`Found ${trs.length} ${autoPlural("locale", trs)}:`, trs.join(", "));
 
-  const { en_US, ...restLocs } = translations;
+  const { "en-US": enUS, ...restLocs } = translations;
   const progress = {} as Record<TrLocale, number>;
 
   //#region progress table
 
   const progTableLines: string[] = [];
 
-  for(const [locale, translations] of Object.entries({ en_US, ...restLocs })) {
-    for(const [k] of Object.entries(en_US)) {
+  for(const [locale, translations] of Object.entries({ "en-US": enUS, ...restLocs })) {
+    for(const [k] of Object.entries(enUS)) {
       if(translations[k]) {
         if(!progress[locale as TrLocale])
           progress[locale as TrLocale] = 0;
@@ -54,7 +54,7 @@ async function run() {
     }
 
     const trKeys = progress[locale as TrLocale];
-    const origKeys = Object.keys(en_US).length;
+    const origKeys = Object.keys(enUS).length;
     const percent = Number(mapRange(trKeys, 0, origKeys, 0, 100).toFixed(1));
 
     const sym = trKeys === origKeys
@@ -68,12 +68,12 @@ async function run() {
     const baseTr = trFiles[locale as TrLocale]?.base;
 
     const keysCol = (
-      locale === "en_US"
+      locale === "en-US"
         ? `\`${origKeys}\` (default locale)`
         : `\`${trKeys}/${origKeys}\` (${percent}%)`
     );
 
-    progTableLines.push(`| ${locale === "en_US" || baseTr ? "" : sym} | [\`${locale}\`](./${locale}.json) | ${keysCol} | ${baseTr ? `\`${baseTr}\`` : (locale === "en_US" ? "" : "─")} |`);
+    progTableLines.push(`| ${locale === "en-US" || baseTr ? "" : sym} | [\`${locale}\`](./${locale}.json) | ${keysCol} | ${baseTr ? `\`${baseTr}\`` : (locale === "en-US" ? "" : "─")} |`);
     console.log(`  ${sym} ${locale}: ${trKeys}/${origKeys} (${percent}%)${baseTr ? ` (base: ${baseTr})`: ""}`);
   }
 
@@ -81,11 +81,11 @@ async function run() {
 
   const missingKeys = [] as string[];
 
-  for(const [locale, translations] of Object.entries({ en_US, ...restLocs })) {
+  for(const [locale, translations] of Object.entries({ "en-US": enUS, ...restLocs })) {
     const lines = [] as string[];
-    for(const [k] of Object.entries(en_US)) {
+    for(const [k] of Object.entries(enUS)) {
       if(!translations[k])
-        lines.push(`| \`${k}\` | \`${en_US[k].replace(/\n/gm, "\\n")}\` |`);
+        lines.push(`| \`${k}\` | \`${enUS[k].replace(/\n/gm, "\\n")}\` |`);
     }
     if(lines.length > 0) {
       missingKeys.push(`

+ 3 - 3
src/types.ts

@@ -207,11 +207,11 @@ export type PluginDefResolvable = PluginDef | { plugin: Pick<PluginDef["plugin"]
 export type PluginDef = {
   plugin: PluginInfo & {
     /**
-     * Descriptions of at least en_US and optionally any other locale supported by BYTM.  
-     * When an untranslated locale is set, the description will default to the value of en_US
+     * Descriptions of at least en-US and optionally any other locale supported by BYTM.  
+     * When an untranslated locale is set, the description will default to the value of en-US
      */
     description: Partial<Record<keyof typeof locales, string>> & {
-      en_US: string;
+      "en-US": string;
     };
     /** URL to the plugin's icon - recommended size: 48x48 to 128x128 */
     iconUrl?: string;

+ 10 - 11
src/utils/misc.ts

@@ -239,32 +239,31 @@ export async function getResourceUrl(name: ResourceKey | "_") {
 
 /**
  * Returns the preferred locale of the user, provided it is supported by the userscript.  
- * Prioritizes `navigator.language`, then `navigator.languages`, then `"en_US"` as a fallback.
+ * Prioritizes `navigator.language`, then `navigator.languages`, then `"en-US"` as a fallback.
  */
 export function getPreferredLocale(): TrLocale {
-  const navLang = navigator.language.replace(/-/g, "_");
-  const navLangs = navigator.languages
-    .filter(lang => lang.match(/^[a-z]{2}(-|_)[A-Z]$/) !== null)
-    .map(lang => lang.replace(/-/g, "_"));
+  const nvLang = navigator.language;
+  const nvLangs = navigator.languages
+    .filter(lang => lang.match(/^[a-z]{2}(-|_)[A-Z]$/) !== null);
 
-  if(Object.entries(langMapping).find(([key]) => key === navLang))
-    return navLang as TrLocale;
+  if(Object.entries(langMapping).find(([key]) => key === nvLang))
+    return nvLang as TrLocale;
 
-  for(const loc of navLangs) {
+  for(const loc of nvLangs) {
     if(Object.entries(langMapping).find(([key]) => key === loc))
       return loc as TrLocale;
   }
 
-  // if navigator.languages has entries that aren't locale codes in the format xx_XX
+  // if navigator.languages has entries that aren't locale codes in the format xx-XX
   if(navigator.languages.some(lang => lang.match(/^[a-z]{2}$/))) {
-    for(const lang of navLangs) {
+    for(const lang of nvLangs) {
       const foundLoc = Object.entries(langMapping).find(([ key ]) => key.startsWith(lang))?.[0];
       if(foundLoc)
         return foundLoc as TrLocale;
     }
   }
 
-  return "en_US";
+  return "en-US";
 }
 
 /** Returns the content behind the passed resource identifier as a string, for example to be assigned to an element's innerHTML property */

+ 2 - 2
src/utils/translations.ts

@@ -5,7 +5,7 @@ import { getFeature } from "../config.js";
 // eslint-disable-next-line @typescript-eslint/no-unused-vars
 import langMapping from "../../assets/locales.json" with { type: "json" };
 // eslint-disable-next-line @typescript-eslint/no-unused-vars
-import tr_enUS from "../../assets/translations/en_US.json";
+import tr_enUS from "../../assets/translations/en-US.json";
 
 export type TrLocale = keyof typeof langMapping;
 export type TrKey = keyof (typeof tr_enUS["translations"]);
@@ -27,7 +27,7 @@ export async function initTranslations(locale: TrLocale) {
     let fallbackTrans: Partial<typeof tr_enUS> = {};
 
     if(getFeature("localeFallback"))
-      fallbackTrans = await fetchLocaleJson("en_US");
+      fallbackTrans = await fetchLocaleJson("en-US");
 
     // merge with base translations if specified
     const baseTransFile = transFile.base ? await fetchLocaleJson(transFile.base) : undefined;