Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 35 additions & 1 deletion src/languages/lib/correct-translation-content.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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') {
Expand Down Expand Up @@ -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) => {
Expand Down Expand Up @@ -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: `,则为 {%<tag>` → `<tag>`
Expand Down Expand Up @@ -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://')

Expand Down Expand Up @@ -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 (
Expand Down Expand Up @@ -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 }}')
Expand All @@ -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 %}')
Expand All @@ -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) ---
Expand Down
72 changes: 70 additions & 2 deletions src/languages/tests/correct-translation-content.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 %}',
Expand Down Expand Up @@ -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) ───────────────────────────────────────────────
Expand Down Expand Up @@ -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) ──────────────────────────────────────────────────
Expand Down Expand Up @@ -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) ───────────────────────────────────────────────────
Expand Down Expand Up @@ -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) ──────────────────────────────────────────────────
Expand Down Expand Up @@ -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) ──────────────────────────────────────────────────
Expand Down Expand Up @@ -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', () => {
Expand Down
Loading