diff --git a/src/languages/lib/correct-translation-content.ts b/src/languages/lib/correct-translation-content.ts index 0060599e3576..8b3999fcf563 100644 --- a/src/languages/lib/correct-translation-content.ts +++ b/src/languages/lib/correct-translation-content.ts @@ -66,6 +66,12 @@ export function correctTranslatedContentStrings( content = content.replaceAll('{% nota %}', '{% note %}') content = content.replaceAll('{%- nota %}', '{%- note %}') content = content.replaceAll('{%- nota -%}', '{%- note -%}') + // `{% otra %}` / `{%- otra %}` — "another/other" = else + content = content.replaceAll('{% otra %}', '{% else %}') + content = content.replaceAll('{%- otra %}', '{%- else %}') + // `{% encabezados de fila %}` — "row headers" = rowheaders + content = content.replaceAll('{% encabezados de fila %}', '{% rowheaders %}') + content = content.replaceAll('{%- encabezados de fila %}', '{%- rowheaders %}') } if (context.code === 'ja') { @@ -104,6 +110,11 @@ export function correctTranslatedContentStrings( content = content.replaceAll('{% 終了コメント %}', '{% endcomment %}') content = content.replaceAll('{% エンドビジュアルスタジオ %}', '{% endvisualstudio %}') content = content.replaceAll('{% エクリプス %}', '{% eclipse %}') + // `{% それ以外の %}` — truncated form of "in the other case" = else + content = content.replaceAll('{% それ以外の %}', '{% else %}') + content = content.replaceAll('{%- それ以外の %}', '{%- else %}') + // `{% それ以外の場合 ifversion X %}` → `{% elsif X %}` (confused elsif + ifversion) + content = content.replace(/\{% それ以外の場合 ifversion\s+(.+?)\s*%\}/g, '{% elsif $1 %}') // `{%- "supported" %}` → `{%- when "supported" %}` (missing `when`) // Preserves original trim syntax (`{%-` vs `{%`) content = content.replace(/\{%-?\s*"(supported|not_supported|preview)"\s*%\}/g, (match) => { @@ -220,6 +231,9 @@ export function correctTranslatedContentStrings( content = content.replaceAll('{% 数据可重用', '{% data reusables') content = content.replaceAll('{% 其他 %}', '{% else %}') content = content.replaceAll('{% 原始 %}', '{% raw %}') + // `{% 否则 %}` — "otherwise" = else (different Chinese word than 其他) + content = content.replaceAll('{% 否则 %}', '{% else %}') + content = content.replaceAll('{%- 否则 %}', '{%- else %}') // Chinese `如果` = "if": `{ 如果 X %}` → `{% if X %}` content = content.replace(/\{ 如果 /g, '{% if ') // Stray Chinese `,则为` ("then") merged with `{%` before HTML: `,则为 {%` → `` @@ -305,13 +319,17 @@ export function correctTranslatedContentStrings( content = content.replaceAll('{% данных для повторного использования.', '{% data reusables.') 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('{% нарисовать %}', '{% endraw %}') content = content.replaceAll('{% эндкёрл %}', '{% endcurl %}') content = content.replaceAll('{% запроса %}', '{% endraw %}') - + // `{% Mac %}` — capitalized mac platform tag + content = content.replaceAll('{% Mac %}', '{% mac %}') // Fix double quotes in Russian YAML files that cause parsing errors content = content.replace(/href=""https:\/\//g, 'href="https://') @@ -386,6 +404,9 @@ export function correctTranslatedContentStrings( content = content.replaceAll('{% conseil %}', '{% tip %}') content = content.replaceAll('{%- conseil %}', '{%- tip %}') content = content.replaceAll('{%- conseil -%}', '{%- tip -%}') + // `{% sinon %}` / `{%- sinon %}` — French "otherwise" = else + content = content.replaceAll('{% sinon %}', '{% else %}') + content = content.replaceAll('{%- sinon %}', '{%- else %}') // Remove orphaned {% endif %} tags when no ifversion/elsif opener exists in the content. // Caused by translations where only the closing tag survived (e.g. user-api.md reusable). if ( @@ -416,6 +437,11 @@ export function correctTranslatedContentStrings( content = content.replace(/\{%-? (?:ifversion|elsif|if) [^%]*?또는[^%]*?%\}/g, (match) => { return match.replace(/ 또는 /g, ' or ') }) + // `{% 그렇지 않으면 %}` — "otherwise" = else + content = content.replaceAll('{% 그렇지 않으면 %}', '{% else %}') + content = content.replaceAll('{%- 그렇지 않으면 %}', '{%- else %}') + // `{% 옥티콘` — Korean transliteration of "octicon" + content = content.replaceAll('{% 옥티콘 ', '{% octicon ') // Korean translation of github-glossary.md content = content.replaceAll('{{ 용어집.term }}', '{{ glossary.term }}') @@ -431,6 +457,8 @@ export function correctTranslatedContentStrings( content = content.replaceAll('{% data wiederverwendbare.', '{% data reusables.') content = content.replaceAll('{% Daten wiederverwendbare.', '{% data reusables.') content = content.replaceAll('{% Data wiederverwendbare.', '{% data reusables.') + // `wiederverwendbar.` (without trailing 'e') — alternate German form + content = content.replaceAll('{% Daten wiederverwendbar.', '{% data reusables.') content = content.replaceAll('{%-Daten variables', '{%- data variables') content = content.replaceAll('{%-Daten-variables', '{%- data variables') content = content.replaceAll('{%- ifversion fpt oder ghec %}', '{%- ifversion fpt or ghec %}') @@ -453,6 +481,12 @@ export function correctTranslatedContentStrings( content = content.replace(/\{%-? für (\w+) in /g, (match) => { return match.replace('für', 'for') }) + // `{% ansonsten %}` / `{%- ansonsten %}` — "otherwise" = else + content = content.replaceAll('{% ansonsten %}', '{% else %}') + content = content.replaceAll('{%- ansonsten %}', '{%- else %}') + // `{% Zeilenkopfzeilen %}` — "row headers" = rowheaders + content = content.replaceAll('{% Zeilenkopfzeilen %}', '{% rowheaders %}') + content = content.replaceAll('{%- Zeilenkopfzeilen %}', '{%- rowheaders %}') } // --- Generic fixes (all languages) --- diff --git a/src/languages/tests/correct-translation-content.ts b/src/languages/tests/correct-translation-content.ts index 282372966786..a1b3cdfa926f 100644 --- a/src/languages/tests/correct-translation-content.ts +++ b/src/languages/tests/correct-translation-content.ts @@ -81,6 +81,16 @@ describe('correctTranslatedContentStrings', () => { expect(fix('{%- nota -%}', 'es')).toBe('{%- note -%}') }) + test('fixes otra → else', () => { + expect(fix('{% otra %}', 'es')).toBe('{% else %}') + expect(fix('{%- otra %}', 'es')).toBe('{%- else %}') + }) + + test('fixes encabezados de fila → rowheaders', () => { + expect(fix('{% encabezados de fila %}', 'es')).toBe('{% rowheaders %}') + expect(fix('{%- encabezados de fila %}', 'es')).toBe('{%- rowheaders %}') + }) + test('fixes multiple or-translations in single ifversion', () => { expect(fix('{% ifversion fpt o ghec o ghes %}', 'es')).toBe( '{% ifversion fpt or ghec or ghes %}', @@ -216,6 +226,21 @@ describe('correctTranslatedContentStrings', () => { // `{ endif% %}` — percent appears after "endif" instead of after the opening brace expect(fix('some content\n{ endif% %}\nmore', 'ja')).toBe('some content\n{% endif %}\nmore') }) + + test('fixes truncated それ以外の → else', () => { + expect(fix('{% それ以外の %}', 'ja')).toBe('{% else %}') + expect(fix('{%- それ以外の %}', 'ja')).toBe('{%- else %}') + }) + + test('fixes それ以外の場合 ifversion X → elsif X', () => { + expect(fix('{% それ以外の場合 ifversion codeql-rust-public-preview %}', 'ja')).toBe( + '{% elsif codeql-rust-public-preview %}', + ) + // no space before closing tag + expect(fix('{% それ以外の場合 ifversion codeql-rust-public-preview%}', 'ja')).toBe( + '{% elsif codeql-rust-public-preview %}', + ) + }) }) // ─── PORTUGUESE (pt) ─────────────────────────────────────────────── @@ -320,6 +345,11 @@ describe('correctTranslatedContentStrings', () => { expect(fix('{% ifversion fpt 或 ghec %}', 'zh')).toBe('{% ifversion fpt or ghec %}') expect(fix('{%- elsif fpt 或 ghec %}', 'zh')).toBe('{%- elsif fpt or ghec %}') }) + + test('fixes 否则 → else', () => { + expect(fix('{% 否则 %}', 'zh')).toBe('{% else %}') + expect(fix('{%- 否则 %}', 'zh')).toBe('{%- else %}') + }) }) // ─── RUSSIAN (ru) ────────────────────────────────────────────────── @@ -474,6 +504,15 @@ describe('correctTranslatedContentStrings', () => { fix('{% octicon "организация" aria-hidden="true" aria-label="organization" %}', 'ru'), ).toBe('{% octicon "organization" aria-hidden="true" aria-label="organization" %}') }) + + test('fixes иначе → else', () => { + expect(fix('{% иначе %}', 'ru')).toBe('{% else %}') + expect(fix('{%- иначе %}', 'ru')).toBe('{%- else %}') + }) + + test('fixes capitalized Mac → mac platform tag', () => { + expect(fix('{% Mac %}', 'ru')).toBe('{% mac %}') + }) }) // ─── FRENCH (fr) ─────────────────────────────────────────────────── @@ -548,6 +587,11 @@ describe('correctTranslatedContentStrings', () => { const input = '{% ifversion fpt %}a{% elsif ghec %}b{% endif %}' expect(fix(input, 'fr')).toBe(input) }) + + test('fixes sinon → else', () => { + expect(fix('{% sinon %}', 'fr')).toBe('{% else %}') + expect(fix('{%- sinon %}', 'fr')).toBe('{%- else %}') + }) }) // ─── KOREAN (ko) ────────────────────────────────────────────────── @@ -592,6 +636,17 @@ describe('correctTranslatedContentStrings', () => { test('fixes Korean glossary template', () => { expect(fix('{{ 용어집.term }}', 'ko')).toBe('{{ glossary.term }}') }) + + test('fixes 그렇지 않으면 → else', () => { + expect(fix('{% 그렇지 않으면 %}', 'ko')).toBe('{% else %}') + expect(fix('{%- 그렇지 않으면 %}', 'ko')).toBe('{%- else %}') + }) + + test('fixes 옥티콘 → octicon', () => { + expect(fix('{% 옥티콘 "check" aria-label="Supported" %}', 'ko')).toBe( + '{% octicon "check" aria-label="Supported" %}', + ) + }) }) // ─── GERMAN (de) ────────────────────────────────────────────────── @@ -657,9 +712,22 @@ describe('correctTranslatedContentStrings', () => { fix('{% Data wiederverwendbare.audit_log.referenz-nach-kategorie-gruppiert %}', 'de'), ).toBe('{% data reusables.audit_log.referenz-nach-kategorie-gruppiert %}') }) - }) + test('fixes wiederverwendbar (without trailing e) reusables path', () => { + expect(fix('{% Daten wiederverwendbar.user-settings.access_settings %}', 'de')).toBe( + '{% data reusables.user-settings.access_settings %}', + ) + }) - // ─── GENERIC FIXES ──────────────────────────────────────────────── + test('fixes ansonsten → else', () => { + expect(fix('{% ansonsten %}', 'de')).toBe('{% else %}') + expect(fix('{%- ansonsten %}', 'de')).toBe('{%- else %}') + }) + + test('fixes Zeilenkopfzeilen → rowheaders', () => { + expect(fix('{% Zeilenkopfzeilen %}', 'de')).toBe('{% rowheaders %}') + expect(fix('{%- Zeilenkopfzeilen %}', 'de')).toBe('{%- rowheaders %}') + }) + }) describe('Generic fixes (all languages)', () => { test('strips LLM sentinel markers and preserves word boundaries', () => {