From f5cdd97b03eeff51c7b444846f4c8413d20ea29a Mon Sep 17 00:00:00 2001 From: docs-bot <77750099+docs-bot@users.noreply.github.com> Date: Mon, 20 Apr 2026 01:29:58 -0700 Subject: [PATCH 1/3] Update docs changelog (for PR #60592) (#60789) Co-authored-by: github-actions[bot] Co-authored-by: hubwriter Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- CHANGELOG.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index cfe7e21b5986..3a42d77f0ea6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,16 @@ # Docs changelog +**15 April 2026** + +LSP servers greatly improve Copilot CLI's ability to work with your code. For example, when Copilot renames a symbol throughout a project it can do this more quickly and reliably if it has access to an LSP server for the language the code is written in. + +We've added documentation to explain LSP servers in greater detail and show how to install these for Copilot CLI: + +* [Using LSP servers with GitHub Copilot CLI](https://docs.github.com/copilot/concepts/agents/copilot-cli/lsp-servers) +* [Adding LSP servers for GitHub Copilot CLI](https://docs.github.com/copilot/how-tos/copilot-cli/set-up-copilot-cli/add-lsp-servers) + +
+ **13 April 2026** To coincide with the release of the ability to [steer your Copilot CLI sessions remotely](https://github.blog/changelog/2026-04-13-remote-control-cli-sessions-on-web-and-mobile-in-public-preview/) (from GitHub.com, or from GitHub Mobile), we have added these new articles: From 1af8a1c47a1d9587ccd6bb871a6ae0bacceb1da1 Mon Sep 17 00:00:00 2001 From: Thom Wong <101249231+supergranular@users.noreply.github.com> Date: Mon, 20 Apr 2026 10:42:09 +0200 Subject: [PATCH 2/3] EDI revisions to reference (#60692) --- .../style-guide-and-content-model/index.md | 2 +- ...tent-type.md => reference-content-type.md} | 30 +++++++++++-------- .../writing-for-github-docs/templates.md | 8 ++--- 3 files changed, 22 insertions(+), 18 deletions(-) rename content/contributing/style-guide-and-content-model/{referential-content-type.md => reference-content-type.md} (56%) diff --git a/content/contributing/style-guide-and-content-model/index.md b/content/contributing/style-guide-and-content-model/index.md index 0eb48b6eee14..f22a95f2bb17 100644 --- a/content/contributing/style-guide-and-content-model/index.md +++ b/content/contributing/style-guide-and-content-model/index.md @@ -10,7 +10,7 @@ children: - /about-the-content-model - /contents-of-a-github-docs-article - /conceptual-content-type - - /referential-content-type + - /reference-content-type - /how-to-content-type - /troubleshooting-content-type - /release-note-content-type diff --git a/content/contributing/style-guide-and-content-model/referential-content-type.md b/content/contributing/style-guide-and-content-model/reference-content-type.md similarity index 56% rename from content/contributing/style-guide-and-content-model/referential-content-type.md rename to content/contributing/style-guide-and-content-model/reference-content-type.md index 28763a11023b..e1bfb4a5cce0 100644 --- a/content/contributing/style-guide-and-content-model/referential-content-type.md +++ b/content/contributing/style-guide-and-content-model/reference-content-type.md @@ -1,6 +1,6 @@ --- -title: Referential content type -intro: Referential content provides detailed information that people need while they are actively using a feature. +title: Reference content type +intro: Reference content provides detailed information that people need while they are actively using a feature. versions: fpt: '*' ghec: '*' @@ -8,17 +8,20 @@ versions: category: - Follow the style guide and content model --- +Reference content is consulted for specific pieces of information. It’s information you can quickly check, meaning there’s less emphasis on sentences and paragraphs. -We create referential articles and referential sections within other articles. -* Some major subjects may require their own referential article, especially if there is a large amount of referential content, such as for search syntax or YAML syntax in {% data variables.product.prodname_actions %}. -* For smaller amounts of content or more specific information, like a list of a feature’s supported languages or hardware requirements, use referential sections in context within procedural or conceptual articles. +Reference includes information that may be best represented in tables, lists, or other structured formats. We might think of reference as including our autogenerated pipeline content and other content that could potentially be automated. -## How to write referential content +Reference content appears in reference articles and reference sections within other articles. +* Some major subjects may require their own reference article, especially if there is a large amount of reference content, such as for search syntax or YAML syntax in {% data variables.product.prodname_actions %}. +* For smaller amounts of content or more specific information, like a list of a feature’s supported languages or hardware requirements, use reference sections in context within procedural or conceptual articles. -For the referential content template, see [AUTOTITLE](/contributing/writing-for-github-docs/templates#referential-article-template). +## How to write reference content -* Write a sentence or an entire conceptual section to introduce the referential content. -* Present the actual referential content clearly and consistently. +For the reference content template, see [AUTOTITLE](/contributing/writing-for-github-docs/templates#reference-article-template). + +* Write a sentence or an entire conceptual section to introduce the reference content. +* Present the actual reference content clearly and consistently. * For subjects with a single element to explain, use a list. * Example: [AUTOTITLE](/organizations/managing-access-to-your-organizations-repositories/repository-permission-levels-for-an-organization#repository-roles-for-organizations) * For subjects with multiple elements to explain, use a table. @@ -33,14 +36,15 @@ For the referential content template, see [AUTOTITLE](/contributing/writing-for- * Referential articles or headers of referential sections clearly describe the contents of the section, and generally begin with nouns. * Titles include enough information to be accessible to novice users and fully describe the contents of each section. * Titles avoid stacked nouns - use prepositions to break up long strings of nouns. +* Short titles should be one word or a short noun phrase. Example: "AI Models". -## Examples of referential content +## Examples of reference content -* Referential articles - * [AUTOTITLE](/get-started/accessibility/keyboard-shortcuts) +* Reference articles + * [AUTOTITLE](/organizations/keeping-your-organization-secure/managing-security-settings-for-your-organization/audit-log-events-for-your-organization) * [AUTOTITLE](/enterprise-cloud@latest/admin/user-management/managing-users-in-your-enterprise/roles-in-an-enterprise) * [AUTOTITLE](/free-pro-team@latest/rest/reference/billing) in the REST API documentation * [AUTOTITLE](/graphql/reference/mutations) in the GraphQL API documentation -* Referential sections within other articles +* Reference sections within other articles * "Supported languages" in [AUTOTITLE](/free-pro-team@latest/get-started/using-github/github-mobile#supported-languages-for-github-mobile) * "Hardware considerations" in [AUTOTITLE](/enterprise-server@latest/admin/installation/installing-github-enterprise-server-on-aws#hardware-considerations) diff --git a/content/contributing/writing-for-github-docs/templates.md b/content/contributing/writing-for-github-docs/templates.md index e4616da42f58..b12e93a9f868 100644 --- a/content/contributing/writing-for-github-docs/templates.md +++ b/content/contributing/writing-for-github-docs/templates.md @@ -59,9 +59,9 @@ Optionally, include a bulleted list of related articles the user can reference t -## Referential article template +## Reference article template -Use the content model for full instructions and examples on how to write referential content. For more information, see [AUTOTITLE](/contributing/style-guide-and-content-model/referential-content-type). +Use the content model for full instructions and examples on how to write reference content. For more information, see [AUTOTITLE](/contributing/style-guide-and-content-model/reference-content-type). @@ -77,8 +77,8 @@ versions: --- {% comment %} -Follow the guidelines in https://docs.github.com/contributing/writing-for-github-docs/content-model#referential to write this article.-- > -Great intros give readers a quick understanding of what's in the article, so they can tell whether it's relevant to them before moving ahead. For more tips, see https://docs.github.com/contributing/writing-for-github-docs/content-model +Follow the guidelines in https://docs.github.com/contributing/writing-for-github-docs/content-model to write this article.-- > +Great intros give readers a quick understanding of what's in the article, so they can tell whether it's relevant to them before moving ahead. For more tips, see https://docs.github.com/en/contributing/style-guide-and-content-model/contents-of-a-github-docs-article#intro For product callout info, see https://github.com/github/docs/tree/main/content#product For product version instructions, see https://github.com/github/docs/tree/main/content#versioning Remove these comments from your article file when you're done writing From 63550e1259e3a13df25fe517f82a95881fe81742 Mon Sep 17 00:00:00 2001 From: Kevin Heis Date: Mon, 20 Apr 2026 07:31:11 -0700 Subject: [PATCH 3/3] =?UTF-8?q?=F0=9F=8C=8E=20Fix=20translation=20corrupti?= =?UTF-8?q?on=20rules:=20add=20dash=20variants,=20remove=20redundancies=20?= =?UTF-8?q?(#60816)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../lib/correct-translation-content.ts | 134 +++++++++--------- .../tests/correct-translation-content.ts | 130 ++++++++++++++--- 2 files changed, 178 insertions(+), 86 deletions(-) diff --git a/src/languages/lib/correct-translation-content.ts b/src/languages/lib/correct-translation-content.ts index ed9baaf11c90..fca1adefd0f9 100644 --- a/src/languages/lib/correct-translation-content.ts +++ b/src/languages/lib/correct-translation-content.ts @@ -43,10 +43,9 @@ export function correctTranslatedContentStrings( content = content.replaceAll('{% Datos variables', '{% data variables') // `{% dato ` — singular form of "datos" = data content = content.replaceAll('{% dato variables', '{% data variables') - // `{% variables.` — missing "data" prefix - content = content.replaceAll('{% variables.', '{% data variables.') // Translated Liquid keywords content = content.replaceAll('{% comentario %}', '{% comment %}') + content = content.replaceAll('{%- comentario %}', '{%- comment %}') content = content.replaceAll('{% si ', '{% if ') content = content.replaceAll('{% sin procesar %}', '{% raw %}') content = content.replaceAll('{% %} sin procesar', '{% raw %}') @@ -110,6 +109,7 @@ export function correctTranslatedContentStrings( content = content.replaceAll('{% データ再利用可能.', '{% data reusables.') content = content.replaceAll('{% データ再利用.', '{% data reusables.') content = content.replaceAll('{% メモ %}', '{% note %}') + content = content.replaceAll('{%- メモ %}', '{%- note %}') // Double-brace corruption of `{% data`: `{% {{データ}} variables.` → `{% data variables.` content = content.replaceAll('{{データ}} variables.', 'data variables.') // Catch "または" between any plan names in ifversion/elsif tags @@ -248,7 +248,9 @@ export function correctTranslatedContentStrings( content = content.replaceAll('{{% dados ', '{% data ') content = content.replaceAll('{{% datas ', '{% data ') content = content.replaceAll('{% senão %}', '{% else %}') + content = content.replaceAll('{%- senão %}', '{%- else %}') content = content.replaceAll('{% mais %}', '{% else %}') + content = content.replaceAll('{%- mais %}', '{%- else %}') content = content.replaceAll('{% se ', '{% if ') content = content.replaceAll('{% atribuir ', '{% assign ') content = content.replaceAll('{% %} bruto', '{% raw %}') @@ -268,6 +270,7 @@ export function correctTranslatedContentStrings( content = content.replaceAll('{%- observação %}', '{%- note %}') // `{% comentário %}` — Portuguese "comment" = comment content = content.replaceAll('{% comentário %}', '{% comment %}') + content = content.replaceAll('{%- comentário %}', '{%- comment %}') // `{% nota de fim %}` — Portuguese "end note" = endnote content = content.replaceAll('{% nota de fim %}', '{% endnote %}') content = content.replaceAll('{%- nota de fim %}', '{%- endnote %}') @@ -294,7 +297,9 @@ export function correctTranslatedContentStrings( content = content.replaceAll('{% 数据可重用s.', '{% data reusables.') content = content.replaceAll('{% 数据可重用', '{% data reusables') content = content.replaceAll('{% 其他 %}', '{% else %}') + content = content.replaceAll('{%- 其他 %}', '{%- else %}') content = content.replaceAll('{% 原始 %}', '{% raw %}') + content = content.replaceAll('{%- 原始 %}', '{%- raw %}') // `{% 否则 %}` — "otherwise" = else (different Chinese word than 其他) content = content.replaceAll('{% 否则 %}', '{% else %}') content = content.replaceAll('{%- 否则 %}', '{%- else %}') @@ -350,6 +355,7 @@ export function correctTranslatedContentStrings( // Stray `,` replacing `data` content = content.replaceAll('{%, variables.', '{% data variables.') content = content.replaceAll('{% необработанного %}', '{% raw %}') + content = content.replaceAll('{%- необработанного %}', '{%- raw %}') content = content.replaceAll('{%- ifversion fpt или ghec %}', '{%- ifversion fpt or ghec %}') content = content.replaceAll('{% ifversion fpt или ghec %}', '{% ifversion fpt or ghec %}') content = content.replaceAll('{% ifversion ghec или fpt %}', '{% ifversion ghec or fpt %}') @@ -359,8 +365,8 @@ export function correctTranslatedContentStrings( content = content.replace(/\{%-? (?:ifversion|elsif|if) [^%]*?или[^%]*?%\}/g, (match) => { return match.replace(/ или /g, ' or ') }) - content = content.replaceAll('{% endif _%}', '{% endif %}') content = content.replaceAll('{% конечным %}', '{% endif %}') + content = content.replaceAll('{%- конечным %}', '{%- endif %}') // `{%- конец %}` — dash-trimmed form of "end" = endif content = content.replaceAll('{%- конец %}', '{%- endif %}') // `{%- конец для %}` — "end for" = endfor @@ -382,6 +388,7 @@ export function correctTranslatedContentStrings( content = content.replaceAll('{% конец %}', '{% endif %}') // Cyrillic transliteration of `elsif` (lossy → else, since version param is lost) content = content.replaceAll('{% Эльсиф %}', '{% else %}') + content = content.replaceAll('{%- Эльсиф %}', '{%- else %}') // Translated feature flag names content = content.replaceAll( 'обязательный-2fa-dotcom-участник', @@ -396,40 +403,31 @@ export function correctTranslatedContentStrings( content = content.replaceAll('{% переменных данных.', '{% data variables.') content = content.replaceAll('{% повторно используемых данных.', '{% data reusables.') content = content.replaceAll('{% примечание %}', '{% note %}') + content = content.replaceAll('{%- примечание %}', '{%- note %}') content = content.replaceAll('{% конечных головщиков %}', '{% endrowheaders %}') content = content.replaceAll('{% данных для повторного использования.', '{% data reusables.') content = content.replaceAll('{% еще %}', '{% else %}') + content = content.replaceAll('{%- еще %}', '{%- else %}') content = content.replaceAll('{% ещё %}', '{% else %}') + content = content.replaceAll('{%- ещё %}', '{%- else %}') // `{% иначе %}` — "otherwise" = else content = content.replaceAll('{% иначе %}', '{% else %}') content = content.replaceAll('{%- иначе %}', '{%- else %}') content = content.replaceAll('{% необработанные %}', '{% raw %}') + content = content.replaceAll('{%- необработанные %}', '{%- raw %}') content = content.replaceAll('{% необработанный %}', '{% raw %}') + content = content.replaceAll('{%- необработанный %}', '{%- raw %}') content = content.replaceAll('{% сырой %}', '{% raw %}') + content = content.replaceAll('{%- сырой %}', '{%- raw %}') content = content.replaceAll('{% нарисовать %}', '{% endraw %}') + content = content.replaceAll('{%- нарисовать %}', '{%- endraw %}') content = content.replaceAll('{% эндкёрл %}', '{% endcurl %}') + content = content.replaceAll('{%- эндкёрл %}', '{%- endcurl %}') content = content.replaceAll('{% запроса %}', '{% endraw %}') - // `{% Mac %}` — capitalized mac platform tag - content = content.replaceAll('{% Mac %}', '{% mac %}') - // `{% Endwindows %}` — capitalized endwindows - content = content.replaceAll('{% Endwindows %}', '{% endwindows %}') - content = content.replaceAll('{%- Endwindows %}', '{%- endwindows %}') - // `{% Elsif ` — capitalized elsif - content = content.replace(/\{% Elsif /g, '{% elsif ') - // `{% Linux %}` — capitalized linux platform tag - content = content.replaceAll('{% Linux %}', '{% linux %}') - content = content.replaceAll('{%- Linux %}', '{%- linux %}') + content = content.replaceAll('{%- запроса %}', '{%- endraw %}') // `{% джетмозги %}` — Russian literal translation of "JetBrains" (джет=jet, мозги=brains) content = content.replaceAll('{% джетмозги %}', '{% jetbrains %}') content = content.replaceAll('{%- джетмозги %}', '{%- jetbrains %}') - // Fix double quotes in Russian YAML files that cause parsing errors - content = content.replace(/href=""https:\/\//g, 'href="https://') - - // Fix empty HTML tags that cause YAML parsing issues - content = content.replaceAll('', '') - content = content.replaceAll('', '') - content = content.replace(/early_access:\s*"([^"]*)<\/b>([^"]*)"/, 'early_access: "$1$2"') - content = content.replace(/(privacy_disclaimer:[^<]*)<\/u>/g, '$1') // Russian translation of github-glossary.md content = content.replaceAll( @@ -470,9 +468,12 @@ export function correctTranslatedContentStrings( content = content.replaceAll('{% données réutilisables.', '{% data reusables.') content = content.replaceAll('{% variables de données.', '{% data variables.') content = content.replaceAll('{% autre %}', '{% else %}') + content = content.replaceAll('{%- autre %}', '{%- else %}') content = content.replaceAll('{% brut %}', '{% raw %}') + content = content.replaceAll('{%- brut %}', '{%- raw %}') content = content.replaceAll('{% %brut }', '{% raw %}') content = content.replaceAll('{% redessiner %}', '{% endraw %}') + content = content.replaceAll('{%- redessiner %}', '{%- endraw %}') content = content.replaceAll('{% données ', '{% data ') // `{% Données ` — capitalized form content = content.replaceAll('{% Données variables', '{% data variables') @@ -553,13 +554,12 @@ export function correctTranslatedContentStrings( content = content.replaceAll('{% 데이터 reusables.', '{% data reusables.') content = content.replaceAll('{% 데이터 변수.', '{% data variables.') content = content.replaceAll('{% 데이터 변숫값.', '{% data variables.') - content = content.replaceAll('{% dada variables', '{% data variables') - // Extra `%` before data: `{% % data` → `{% data` - content = content.replaceAll('{% % data', '{% data') content = content.replaceAll('{% 기타 %}', '{% else %}') content = content.replaceAll('{%- 기타 %}', '{%- else %}') content = content.replaceAll('{% 참고 %}', '{% note %}') + content = content.replaceAll('{%- 참고 %}', '{%- note %}') content = content.replaceAll('{% 원시 %}', '{% raw %}') + content = content.replaceAll('{%- 원시 %}', '{%- raw %}') // Catch "또는" between any plan names in ifversion/elsif/if tags content = content.replace(/\{%-? (?:ifversion|elsif|if) [^%]*?또는[^%]*?%\}/g, (match) => { return match.replace(/ 또는 /g, ' or ') @@ -569,13 +569,12 @@ export function correctTranslatedContentStrings( content = content.replaceAll('{%- 그렇지 않으면 %}', '{%- else %}') // `{% 옥티콘` — Korean transliteration of "octicon" content = content.replaceAll('{% 옥티콘 ', '{% octicon ') + content = content.replaceAll('{%- 옥티콘 ', '{%- octicon ') // Korean translation of github-glossary.md content = content.replaceAll('{{ 용어집.term }}', '{{ glossary.term }}') // `{% 데이터 재사용.` — Korean translation of "data reusables" path content = content.replaceAll('{% 데이터 재사용.', '{% data reusables.') - // `{% datavariable` — compound missing space and plural: "datavariable" → "data variables" - content = content.replaceAll('{% datavariable', '{% data variables') // `{% 행 머리글 %}` — "row headers" = rowheaders content = content.replaceAll('{% 행 머리글 %}', '{% rowheaders %}') content = content.replaceAll('{%- 행 머리글 %}', '{%- rowheaders %}') @@ -593,13 +592,13 @@ export function correctTranslatedContentStrings( if (context.code === 'de') { content = content.replaceAll('{% Daten variables', '{% data variables') content = content.replaceAll('{% daten variables', '{% data variables') - content = content.replaceAll('{% Data variables', '{% data variables') content = content.replaceAll('{% Daten reusables', '{% data reusables') - content = content.replaceAll('{% Data reusables', '{% data reusables') + content = content.replaceAll('{%- Daten reusables', '{%- data reusables') // `wiederverwendbare` is German for "reusables" — fix translated reusables paths content = content.replaceAll('{% data wiederverwendbare.', '{% data reusables.') content = content.replaceAll('{% Daten wiederverwendbare.', '{% data reusables.') content = content.replaceAll('{% Data wiederverwendbare.', '{% data reusables.') + content = content.replaceAll('{%- Daten wiederverwendbare.', '{%- data reusables.') // `wiederverwendbar.` (without trailing 'e') — alternate German form content = content.replaceAll('{% Daten wiederverwendbar.', '{% data reusables.') // `daten wiederverwendbars.` — lowercase with trailing 's' @@ -613,6 +612,7 @@ export function correctTranslatedContentStrings( content = content.replaceAll('{% Daten Variablen.', '{% data variables.') // `{% daten reusables` — lowercase with English "reusables" content = content.replaceAll('{% daten reusables', '{% data reusables') + content = content.replaceAll('{%- daten reusables', '{%- data reusables') // `{% unformatierte %}` — "unformatted" = raw content = content.replaceAll('{% unformatierte %}', '{% raw %}') content = content.replaceAll('{%- unformatierte %}', '{%- raw %}') @@ -659,21 +659,9 @@ export function correctTranslatedContentStrings( content = content.replaceAll('{% Rohdaten %}', '{% raw %}') content = content.replaceAll('{%- Rohdaten %}', '{%- raw %}') content = content.replaceAll('{%- Rohdaten -%}', '{%- raw -%}') - // `{% okticon ` — "octicon" transliterated to "okticon" - content = content.replaceAll('{% okticon ', '{% octicon ') // `{% Endnotiz %}` — "end note" = endnote content = content.replaceAll('{% Endnotiz %}', '{% endnote %}') content = content.replaceAll('{%- Endnotiz %}', '{%- endnote %}') - // `{% endifen %}` — garbled "endif" = endif - content = content.replaceAll('{% endifen %}', '{% endif %}') - content = content.replaceAll('{%- endifen %}', '{%- endif %}') - // `{% Endifen %}` — capitalized variant - content = content.replaceAll('{% Endifen %}', '{% endif %}') - content = content.replaceAll('{%- Endifen %}', '{%- endif %}') - // `{% Endif %}` — capitalized endif - content = content.replaceAll('{% Endif %}', '{% endif %}') - content = content.replaceAll('{%- Endif %}', '{%- endif %}') - content = content.replaceAll('{%- Endif -%}', '{%- endif -%}') // `{% Dateninstanz` — "data instance" = data content = content.replaceAll('{% Dateninstanz ', '{% data ') // `{% ifversion-Sicherheitskonfigurationen %}` — hyphenated compound @@ -697,33 +685,6 @@ export function correctTranslatedContentStrings( '{%- ifversion-Sicherheitskampagnen %}', '{%- ifversion security-campaigns %}', ) - // `{% ifversion-repo-policy-rules %}` — missing space before feature flag - content = content.replaceAll( - '{% ifversion-repo-policy-rules %}', - '{% ifversion repo-policy-rules %}', - ) - content = content.replaceAll( - '{%- ifversion-repo-policy-rules %}', - '{%- ifversion repo-policy-rules %}', - ) - // `{% ifversion-enterprise-installed-apps %}` — missing space before feature flag - content = content.replaceAll( - '{% ifversion-enterprise-installed-apps %}', - '{% ifversion enterprise-installed-apps %}', - ) - content = content.replaceAll( - '{%- ifversion-enterprise-installed-apps %}', - '{%- ifversion enterprise-installed-apps %}', - ) - // `{% Windows %}` — capitalized platform tag - content = content.replaceAll('{% Windows %}', '{% windows %}') - content = content.replaceAll('{%- Windows %}', '{%- windows %}') - // `{% Linux %}` — capitalized platform tag - content = content.replaceAll('{% Linux %}', '{% linux %}') - content = content.replaceAll('{%- Linux %}', '{%- linux %}') - // `{% Eclipse %}` — capitalized platform tag - content = content.replaceAll('{% Eclipse %}', '{% eclipse %}') - content = content.replaceAll('{%- Eclipse %}', '{%- eclipse %}') } // --- Generic fixes (all languages) --- @@ -734,8 +695,9 @@ export function correctTranslatedContentStrings( // words don't concatenate. content = content.replace(/\s*<\|endoftext\|>\s*/g, ' ') - // Capitalized Liquid keyword: `{% Data ` → `{% data ` + // Capitalized Liquid keyword: `{% Data ` / `{%- Data ` → `{% data ` / `{%- data ` content = content.replaceAll('{% Data ', '{% data ') + content = content.replaceAll('{%- Data ', '{%- data ') // Capitalized platform tags (cross-language) content = content.replaceAll('{% Windows %}', '{% windows %}') @@ -744,6 +706,40 @@ export function correctTranslatedContentStrings( content = content.replaceAll('{%- Linux %}', '{%- linux %}') content = content.replaceAll('{% Eclipse %}', '{% eclipse %}') content = content.replaceAll('{%- Eclipse %}', '{%- eclipse %}') + content = content.replaceAll('{% Mac %}', '{% mac %}') + content = content.replaceAll('{%- Mac %}', '{%- mac %}') + + // Capitalized Liquid keywords (cross-language) + content = content.replaceAll('{% Endwindows %}', '{% endwindows %}') + content = content.replaceAll('{%- Endwindows %}', '{%- endwindows %}') + content = content.replace(/\{% Elsif /g, '{% elsif ') + content = content.replaceAll('{% Endif %}', '{% endif %}') + content = content.replaceAll('{%- Endif %}', '{%- endif %}') + content = content.replaceAll('{%- Endif -%}', '{%- endif -%}') + + // Garbled "endif" variants + content = content.replaceAll('{% endifen %}', '{% endif %}') + content = content.replaceAll('{%- endifen %}', '{%- endif %}') + content = content.replaceAll('{% Endifen %}', '{% endif %}') + content = content.replaceAll('{%- Endifen %}', '{%- endif %}') + content = content.replaceAll('{% endif _%}', '{% endif %}') + + // Near-miss "octicon" typo + content = content.replaceAll('{% okticon ', '{% octicon ') + + // Typos in "data" keyword + content = content.replaceAll('{% dada variables', '{% data variables') + content = content.replaceAll('{% % data', '{% data') + + // Double-quote corruption in href attributes + content = content.replace(/href=""https:\/\//g, 'href="https://') + + // Empty HTML tags that cause parsing issues + content = content.replaceAll('', '') + content = content.replaceAll('', '') + + // `{% ifversion-FEATURE %}` — hyphen instead of space before lowercase feature flag + content = content.replace(/(\{%-? )ifversion-([a-z][\w-]*\s*%\})/g, '$1ifversion $2') // These run after per-language fixes so that e.g. `{{% данных variables` // first becomes `{{% data variables` and then gets caught here. @@ -810,6 +806,8 @@ export function correctTranslatedContentStrings( content = content.replaceAll('{% variables.', '{% data variables.') content = content.replaceAll('{% reusables.', '{% data reusables.') content = content.replaceAll('{% datavariables', '{% data variables') + content = content.replaceAll('{% datavariable.', '{% data variables.') + content = content.replaceAll('{% datavariable ', '{% data variables ') // Empty `{% %}` corruptions where the tag name was removed. content = content.replaceAll('{% %} de dados reusables.', '{% data reusables.') diff --git a/src/languages/tests/correct-translation-content.ts b/src/languages/tests/correct-translation-content.ts index 12e7244705dd..308391c5bb5b 100644 --- a/src/languages/tests/correct-translation-content.ts +++ b/src/languages/tests/correct-translation-content.ts @@ -34,6 +34,7 @@ describe('correctTranslatedContentStrings', () => { test('fixes translated comment keyword', () => { expect(fix('{% comentario %}', 'es')).toBe('{% comment %}') + expect(fix('{%- comentario %}', 'es')).toBe('{%- comment %}') }) test('fixes translated if keyword', () => { @@ -159,6 +160,7 @@ describe('correctTranslatedContentStrings', () => { test('fixes note keyword', () => { expect(fix('{% メモ %}', 'ja')).toBe('{% note %}') + expect(fix('{%- メモ %}', 'ja')).toBe('{%- note %}') }) test('fixes Japanese or (または) in ifversion tags', () => { @@ -318,7 +320,9 @@ describe('correctTranslatedContentStrings', () => { test('fixes translated else variants', () => { expect(fix('{% senão %}', 'pt')).toBe('{% else %}') + expect(fix('{%- senão %}', 'pt')).toBe('{%- else %}') expect(fix('{% mais %}', 'pt')).toBe('{% else %}') + expect(fix('{%- mais %}', 'pt')).toBe('{%- else %}') }) test('fixes translated if keyword', () => { @@ -395,6 +399,7 @@ describe('correctTranslatedContentStrings', () => { test('fixes comentário → comment', () => { expect(fix('{% comentário %}', 'pt')).toBe('{% comment %}') + expect(fix('{%- comentário %}', 'pt')).toBe('{%- comment %}') }) test('fixes nota de fim → endnote', () => { @@ -433,7 +438,9 @@ describe('correctTranslatedContentStrings', () => { test('fixes translated else and raw', () => { expect(fix('{% 其他 %}', 'zh')).toBe('{% else %}') + expect(fix('{%- 其他 %}', 'zh')).toBe('{%- else %}') expect(fix('{% 原始 %}', 'zh')).toBe('{% raw %}') + expect(fix('{%- 原始 %}', 'zh')).toBe('{%- raw %}') }) test('fixes Chinese if keyword', () => { @@ -536,11 +543,17 @@ describe('correctTranslatedContentStrings', () => { test('fixes translated raw/endraw', () => { expect(fix('{% необработанного %}', 'ru')).toBe('{% raw %}') + expect(fix('{%- необработанного %}', 'ru')).toBe('{%- raw %}') expect(fix('{% необработанные %}', 'ru')).toBe('{% raw %}') + expect(fix('{%- необработанные %}', 'ru')).toBe('{%- raw %}') expect(fix('{% необработанный %}', 'ru')).toBe('{% raw %}') + expect(fix('{%- необработанный %}', 'ru')).toBe('{%- raw %}') expect(fix('{% сырой %}', 'ru')).toBe('{% raw %}') + expect(fix('{%- сырой %}', 'ru')).toBe('{%- raw %}') expect(fix('{% нарисовать %}', 'ru')).toBe('{% endraw %}') + expect(fix('{%- нарисовать %}', 'ru')).toBe('{%- endraw %}') expect(fix('{% запроса %}', 'ru')).toBe('{% endraw %}') + expect(fix('{%- запроса %}', 'ru')).toBe('{%- endraw %}') }) test('fixes translated or (или) in ifversion tags', () => { @@ -557,7 +570,9 @@ describe('correctTranslatedContentStrings', () => { test('fixes translated else variants', () => { expect(fix('{% еще %}', 'ru')).toBe('{% else %}') + expect(fix('{%- еще %}', 'ru')).toBe('{%- else %}') expect(fix('{% ещё %}', 'ru')).toBe('{% else %}') + expect(fix('{%- ещё %}', 'ru')).toBe('{%- else %}') }) test('fixes конец as context-aware end tag', () => { @@ -586,11 +601,14 @@ describe('correctTranslatedContentStrings', () => { test('fixes other translated keywords', () => { expect(fix('{% конечным %}', 'ru')).toBe('{% endif %}') - expect(fix('{% endif _%}', 'ru')).toBe('{% endif %}') + expect(fix('{%- конечным %}', 'ru')).toBe('{%- endif %}') expect(fix('{% примечание %}', 'ru')).toBe('{% note %}') + expect(fix('{%- примечание %}', 'ru')).toBe('{%- note %}') expect(fix('{% конечных головщиков %}', 'ru')).toBe('{% endrowheaders %}') expect(fix('{% эндкёрл %}', 'ru')).toBe('{% endcurl %}') + expect(fix('{%- эндкёрл %}', 'ru')).toBe('{%- endcurl %}') expect(fix('{% Эльсиф %}', 'ru')).toBe('{% else %}') + expect(fix('{%- Эльсиф %}', 'ru')).toBe('{%- else %}') }) test('fixes translated reusable paths', () => { @@ -600,11 +618,11 @@ describe('correctTranslatedContentStrings', () => { ) }) - test('fixes double quotes in Russian YAML', () => { + test('fixes double quotes in Russian YAML (via generic)', () => { expect(fix('href=""https://example.com"', 'ru')).toBe('href="https://example.com"') }) - test('fixes empty HTML tags', () => { + test('fixes empty HTML tags (via generic)', () => { expect(fix('some text', 'ru')).toBe('some text') expect(fix('some text', 'ru')).toBe('some text') }) @@ -632,16 +650,17 @@ describe('correctTranslatedContentStrings', () => { expect(fix('{%- иначе %}', 'ru')).toBe('{%- else %}') }) - test('fixes capitalized Mac → mac platform tag', () => { + test('fixes capitalized Mac → mac platform tag (via generic)', () => { expect(fix('{% Mac %}', 'ru')).toBe('{% mac %}') + expect(fix('{%- Mac %}', 'ru')).toBe('{%- mac %}') }) - test('fixes Endwindows → endwindows', () => { + test('fixes Endwindows → endwindows (via generic)', () => { expect(fix('{% Endwindows %}', 'ru')).toBe('{% endwindows %}') expect(fix('{%- Endwindows %}', 'ru')).toBe('{%- endwindows %}') }) - test('fixes capitalized Elsif → elsif', () => { + test('fixes capitalized Elsif → elsif (via generic)', () => { expect(fix('{% Elsif ghec %}', 'ru')).toBe('{% elsif ghec %}') }) @@ -672,12 +691,15 @@ describe('correctTranslatedContentStrings', () => { test('fixes translated else', () => { expect(fix('{% autre %}', 'fr')).toBe('{% else %}') + expect(fix('{%- autre %}', 'fr')).toBe('{%- else %}') }) test('fixes translated raw/endraw', () => { expect(fix('{% brut %}', 'fr')).toBe('{% raw %}') + expect(fix('{%- brut %}', 'fr')).toBe('{%- raw %}') expect(fix('{% %brut }', 'fr')).toBe('{% raw %}') expect(fix('{% redessiner %}', 'fr')).toBe('{% endraw %}') + expect(fix('{%- redessiner %}', 'fr')).toBe('{%- endraw %}') }) test('fixes ou → or in ifversion tags', () => { @@ -791,13 +813,16 @@ describe('correctTranslatedContentStrings', () => { expect(fix('{% 데이터 변숫값.product.github %}', 'ko')).toBe( '{% data variables.product.github %}', ) - expect(fix('{% dada variables.product.github %}', 'ko')).toBe( + }) + + test('fixes extra percent before data (via generic)', () => { + expect(fix('{% % data variables.product.github %}', 'ko')).toBe( '{% data variables.product.github %}', ) }) - test('fixes extra percent before data', () => { - expect(fix('{% % data variables.product.github %}', 'ko')).toBe( + test('fixes dada → data (via generic)', () => { + expect(fix('{% dada variables.product.github %}', 'ko')).toBe( '{% data variables.product.github %}', ) }) @@ -806,7 +831,9 @@ describe('correctTranslatedContentStrings', () => { expect(fix('{% 기타 %}', 'ko')).toBe('{% else %}') expect(fix('{%- 기타 %}', 'ko')).toBe('{%- else %}') expect(fix('{% 참고 %}', 'ko')).toBe('{% note %}') + expect(fix('{%- 참고 %}', 'ko')).toBe('{%- note %}') expect(fix('{% 원시 %}', 'ko')).toBe('{% raw %}') + expect(fix('{%- 원시 %}', 'ko')).toBe('{%- raw %}') }) test('fixes 또는 → or in ifversion tags', () => { @@ -826,6 +853,9 @@ describe('correctTranslatedContentStrings', () => { expect(fix('{% 옥티콘 "check" aria-label="Supported" %}', 'ko')).toBe( '{% octicon "check" aria-label="Supported" %}', ) + expect(fix('{%- 옥티콘 "check" aria-label="Supported" %}', 'ko')).toBe( + '{%- octicon "check" aria-label="Supported" %}', + ) }) test('fixes 데이터 재사용 → data reusables', () => { @@ -834,7 +864,7 @@ describe('correctTranslatedContentStrings', () => { ) }) - test('fixes datavariable → data variables', () => { + test('fixes datavariable → data variables (via generic)', () => { expect(fix('{% datavariable.product.github %}', 'ko')).toBe( '{% data variables.product.github %}', ) @@ -871,10 +901,8 @@ describe('correctTranslatedContentStrings', () => { expect(fix('{% daten variables.product.github %}', 'de')).toBe( '{% data variables.product.github %}', ) - expect(fix('{% Data variables.product.github %}', 'de')).toBe( - '{% data variables.product.github %}', - ) expect(fix('{% Daten reusables.foo %}', 'de')).toBe('{% data reusables.foo %}') + expect(fix('{%- Daten reusables.foo %}', 'de')).toBe('{%- data reusables.foo %}') }) test('fixes hyphenated data tags without space', () => { @@ -918,6 +946,9 @@ describe('correctTranslatedContentStrings', () => { expect(fix('{% Daten wiederverwendbare.audit_log.reference %}', 'de')).toBe( '{% data reusables.audit_log.reference %}', ) + expect(fix('{%- Daten wiederverwendbare.audit_log.reference %}', 'de')).toBe( + '{%- data reusables.audit_log.reference %}', + ) // Full real-world example: `{% Data wiederverwendbare.audit_log.referenz-nach-kategorie-gruppiert %}` // The `{% Data ` → `{% data ` fix runs before this, so by the time we check: expect( @@ -973,7 +1004,7 @@ describe('correctTranslatedContentStrings', () => { expect(fix('{%- Rohdaten -%}', 'de')).toBe('{%- raw -%}') }) - test('fixes okticon → octicon', () => { + test('fixes okticon → octicon (via generic)', () => { expect(fix('{% okticon "pencil" %}', 'de')).toBe('{% octicon "pencil" %}') }) @@ -982,7 +1013,7 @@ describe('correctTranslatedContentStrings', () => { expect(fix('{%- Endnotiz %}', 'de')).toBe('{%- endnote %}') }) - test('fixes endifen → endif', () => { + test('fixes endifen → endif (via generic)', () => { expect(fix('{% endifen %}', 'de')).toBe('{% endif %}') expect(fix('{%- endifen %}', 'de')).toBe('{%- endif %}') }) @@ -1041,18 +1072,18 @@ describe('correctTranslatedContentStrings', () => { ) }) - test('fixes Endifen and Endif → endif', () => { + test('fixes Endifen and Endif → endif (via generic)', () => { expect(fix('{% Endifen %}', 'de')).toBe('{% endif %}') expect(fix('{% Endif %}', 'de')).toBe('{% endif %}') }) - test('fixes ifversion-repo-policy-rules', () => { + test('fixes ifversion-repo-policy-rules (via generic)', () => { expect(fix('{% ifversion-repo-policy-rules %}', 'de')).toBe( '{% ifversion repo-policy-rules %}', ) }) - test('fixes ifversion-enterprise-installed-apps', () => { + test('fixes ifversion-enterprise-installed-apps (via generic)', () => { expect(fix('{% ifversion-enterprise-installed-apps %}', 'de')).toBe( '{% ifversion enterprise-installed-apps %}', ) @@ -1072,12 +1103,75 @@ describe('correctTranslatedContentStrings', () => { ) expect(fix('{% Data reusables.foo %}', 'es')).toBe('{% data reusables.foo %}') expect(fix('{% Data ifversion ghec %}', 'es')).toBe('{% data ifversion ghec %}') + expect(fix('{%- Data variables.product.github %}', 'es')).toBe( + '{%- data variables.product.github %}', + ) }) test('fixes capitalized platform tags across all languages', () => { expect(fix('{% Windows %}', 'zh')).toBe('{% windows %}') expect(fix('{% Eclipse %}', 'zh')).toBe('{% eclipse %}') expect(fix('{% Linux %}', 'zh')).toBe('{% linux %}') + expect(fix('{% Mac %}', 'zh')).toBe('{% mac %}') + expect(fix('{%- Mac %}', 'zh')).toBe('{%- mac %}') + }) + + test('fixes capitalized Liquid keywords across all languages', () => { + expect(fix('{% Endwindows %}', 'es')).toBe('{% endwindows %}') + expect(fix('{%- Endwindows %}', 'ja')).toBe('{%- endwindows %}') + expect(fix('{% Elsif ghec %}', 'pt')).toBe('{% elsif ghec %}') + expect(fix('{% Endif %}', 'es')).toBe('{% endif %}') + expect(fix('{%- Endif %}', 'ja')).toBe('{%- endif %}') + expect(fix('{%- Endif -%}', 'pt')).toBe('{%- endif -%}') + }) + + test('fixes garbled endif variants across all languages', () => { + expect(fix('{% endifen %}', 'es')).toBe('{% endif %}') + expect(fix('{%- endifen %}', 'ja')).toBe('{%- endif %}') + expect(fix('{% Endifen %}', 'pt')).toBe('{% endif %}') + expect(fix('{%- Endifen %}', 'zh')).toBe('{%- endif %}') + expect(fix('{% endif _%}', 'fr')).toBe('{% endif %}') + }) + + test('fixes okticon → octicon across all languages', () => { + expect(fix('{% okticon "pencil" %}', 'es')).toBe('{% octicon "pencil" %}') + }) + + test('fixes dada → data across all languages', () => { + expect(fix('{% dada variables.product.github %}', 'es')).toBe( + '{% data variables.product.github %}', + ) + }) + + test('fixes extra percent before data across all languages', () => { + expect(fix('{% % data variables.product.github %}', 'es')).toBe( + '{% data variables.product.github %}', + ) + }) + + test('fixes double-quote corruption in href across all languages', () => { + expect(fix('href=""https://example.com"', 'es')).toBe('href="https://example.com"') + }) + + test('fixes empty HTML tags across all languages', () => { + expect(fix('some text', 'es')).toBe('some text') + expect(fix('some text', 'es')).toBe('some text') + }) + + test('fixes ifversion-FEATURE hyphen-instead-of-space across all languages', () => { + expect(fix('{% ifversion-repo-policy-rules %}', 'es')).toBe( + '{% ifversion repo-policy-rules %}', + ) + expect(fix('{%- ifversion-enterprise-installed-apps %}', 'ja')).toBe( + '{%- ifversion enterprise-installed-apps %}', + ) + expect(fix('{% ifversion-some-new-feature %}', 'pt')).toBe('{% ifversion some-new-feature %}') + }) + + test('fixes datavariable (singular) → data variables', () => { + expect(fix('{% datavariable.product.github %}', 'es')).toBe( + '{% data variables.product.github %}', + ) }) test('fixes AUTOTITLE corruption patterns', () => {