From f809052193dd974b088de56a963ae44a1b8fc3f9 Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Tue, 7 Jan 2025 21:42:45 +0900 Subject: [PATCH 01/23] Support the new exit code for `git remote` subcommands for git version >=2.30.0 (#33129) Fix #32889 --------- Co-authored-by: wxiaoguang (cherry picked from commit 0d7d2ed39d0c0435cdc6403ee7764850154dca5a) Conflicts: modules/git/remote.go trivial context conflict --- modules/git/remote.go | 10 ++++++++++ services/mirror/mirror_pull.go | 8 ++++---- services/repository/migrate.go | 7 +++---- 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/modules/git/remote.go b/modules/git/remote.go index 3585313f6a..eea57dd8e0 100644 --- a/modules/git/remote.go +++ b/modules/git/remote.go @@ -5,6 +5,7 @@ package git import ( "context" + "strings" giturl "code.gitea.io/gitea/modules/git/url" ) @@ -37,3 +38,12 @@ func GetRemoteURL(ctx context.Context, repoPath, remoteName string) (*giturl.Git } return giturl.Parse(addr) } + +// IsRemoteNotExistError checks the prefix of the error message to see whether a remote does not exist. +func IsRemoteNotExistError(err error) bool { + // see: https://github.com/go-gitea/gitea/issues/32889#issuecomment-2571848216 + // Should not add space in the end, sometimes git will add a `:` + prefix1 := "exit status 128 - fatal: No such remote" // git < 2.30 + prefix2 := "exit status 2 - error: No such remote" // git >= 2.30 + return strings.HasPrefix(err.Error(), prefix1) || strings.HasPrefix(err.Error(), prefix2) +} diff --git a/services/mirror/mirror_pull.go b/services/mirror/mirror_pull.go index 2d128919f9..41b9516eae 100644 --- a/services/mirror/mirror_pull.go +++ b/services/mirror/mirror_pull.go @@ -40,7 +40,7 @@ func UpdateAddress(ctx context.Context, m *repo_model.Mirror, addr string) error repoPath := m.GetRepository(ctx).RepoPath() // Remove old remote _, _, err = git.NewCommand(ctx, "remote", "rm").AddDynamicArguments(remoteName).RunStdString(&git.RunOpts{Dir: repoPath}) - if err != nil && !strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") { + if err != nil && !git.IsRemoteNotExistError(err) { return err } @@ -51,7 +51,7 @@ func UpdateAddress(ctx context.Context, m *repo_model.Mirror, addr string) error cmd.SetDescription(fmt.Sprintf("remote add %s --mirror=fetch %s [repo_path: %s]", remoteName, addr, repoPath)) } _, _, err = cmd.RunStdString(&git.RunOpts{Dir: repoPath}) - if err != nil && !strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") { + if err != nil && !git.IsRemoteNotExistError(err) { return err } @@ -60,7 +60,7 @@ func UpdateAddress(ctx context.Context, m *repo_model.Mirror, addr string) error wikiRemotePath := repo_module.WikiRemoteURL(ctx, addr) // Remove old remote of wiki _, _, err = git.NewCommand(ctx, "remote", "rm").AddDynamicArguments(remoteName).RunStdString(&git.RunOpts{Dir: wikiPath}) - if err != nil && !strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") { + if err != nil && !git.IsRemoteNotExistError(err) { return err } @@ -71,7 +71,7 @@ func UpdateAddress(ctx context.Context, m *repo_model.Mirror, addr string) error cmd.SetDescription(fmt.Sprintf("remote add %s --mirror=fetch %s [repo_path: %s]", remoteName, wikiRemotePath, wikiPath)) } _, _, err = cmd.RunStdString(&git.RunOpts{Dir: wikiPath}) - if err != nil && !strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") { + if err != nil && !git.IsRemoteNotExistError(err) { return err } } diff --git a/services/repository/migrate.go b/services/repository/migrate.go index 39ced04ae3..b5735ac5b2 100644 --- a/services/repository/migrate.go +++ b/services/repository/migrate.go @@ -8,7 +8,6 @@ import ( "errors" "fmt" "net/http" - "strings" "time" "code.gitea.io/gitea/models/db" @@ -253,10 +252,10 @@ func MigrateRepositoryGitData(ctx context.Context, u *user_model.User, func cleanUpMigrateGitConfig(ctx context.Context, repoPath string) error { cmd := git.NewCommand(ctx, "remote", "rm", "origin") // if the origin does not exist - _, stderr, err := cmd.RunStdString(&git.RunOpts{ + _, _, err := cmd.RunStdString(&git.RunOpts{ Dir: repoPath, }) - if err != nil && !strings.HasPrefix(stderr, "fatal: No such remote") { + if err != nil && !git.IsRemoteNotExistError(err) { return err } return nil @@ -275,7 +274,7 @@ func CleanUpMigrateInfo(ctx context.Context, repo *repo_model.Repository) (*repo } _, _, err := git.NewCommand(ctx, "remote", "rm", "origin").RunStdString(&git.RunOpts{Dir: repoPath}) - if err != nil && !strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") { + if err != nil && !git.IsRemoteNotExistError(err) { return repo, fmt.Errorf("CleanUpMigrateInfo: %w", err) } From d09b8ba9cf190821ee20ec55898625f12d29febe Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Wed, 8 Jan 2025 23:15:47 -0800 Subject: [PATCH 02/23] Fix fuzz test (#33156) (cherry picked from commit ba5e3a5161a497aa8c73827774870fb94676e68b) --- tests/fuzz/fuzz_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/fuzz/fuzz_test.go b/tests/fuzz/fuzz_test.go index 25a6ed8213..d16f26c085 100644 --- a/tests/fuzz/fuzz_test.go +++ b/tests/fuzz/fuzz_test.go @@ -27,6 +27,7 @@ var renderContext = markup.RenderContext{ func FuzzMarkdownRenderRaw(f *testing.F) { f.Fuzz(func(t *testing.T, data []byte) { + setting.IsInTesting = true setting.AppURL = "http://localhost:3000/" markdown.RenderRaw(&renderContext, bytes.NewReader(data), io.Discard) }) @@ -34,6 +35,7 @@ func FuzzMarkdownRenderRaw(f *testing.F) { func FuzzMarkupPostProcess(f *testing.F) { f.Fuzz(func(t *testing.T, data []byte) { + setting.IsInTesting = true setting.AppURL = "http://localhost:3000/" markup.PostProcess(&renderContext, bytes.NewReader(data), io.Discard) }) From f2feb34927889a3e297274235c22238312f66002 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Thu, 9 Jan 2025 22:00:06 -0800 Subject: [PATCH 03/23] chore(performance): loadCommentsByType sets Issues Keep the setting of comment.Issues from the refactor. It is cheap and potentially saves loading the issue again. Former title: Some small refactors (#33144) (cherry picked from commit d3083d21981f9445cf7570956a1fdedfc8578b56) Conflicts: models/issues/comment_list.go models/issues/issue_list.go routers/web/repo/issue_view.go --- models/issues/issue.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/models/issues/issue.go b/models/issues/issue.go index fbbc4828a2..1e969790d7 100644 --- a/models/issues/issue.go +++ b/models/issues/issue.go @@ -268,6 +268,9 @@ func (issue *Issue) loadCommentsByType(ctx context.Context, tp CommentType) (err IssueID: issue.ID, Type: tp, }) + for _, comment := range issue.Comments { + comment.Issue = issue + } return err } From a975b6ab94344d802e72e2ac6a98212794ed9896 Mon Sep 17 00:00:00 2001 From: Otto Richter Date: Tue, 14 Jan 2025 22:13:31 +0100 Subject: [PATCH 04/23] tests(e2e): Explicitly generate screenshots As per https://codeberg.org/forgejo/forgejo/pulls/6400, the after hook runs for every test, resulting in duplicated screenshots. Not all tests are supposed to generate screenshots, especially because they could be flaky (also see https://code.forgejo.org/forgejo/visual-browser-testing/commit/206d4cfb7a4af6d8d7043026cdd4d63708798b2a ). Additionally, the implicit behaviour might have caused confusion, so we now create screenshots explicitly, adding the statements from the tests that already generated screenshots. --- tests/e2e/actions.test.e2e.ts | 1 + tests/e2e/clipboard-copy.test.e2e.ts | 5 ++++- tests/e2e/dashboard-ci-status.test.e2e.ts | 5 +++-- tests/e2e/example.test.e2e.ts | 3 ++- tests/e2e/explore.test.e2e.ts | 3 ++- tests/e2e/markup.test.e2e.ts | 3 ++- tests/e2e/repo-code.test.e2e.ts | 2 ++ tests/e2e/repo-commitgraph.test.e2e.ts | 3 ++- tests/e2e/repo-migrate.test.e2e.ts | 3 ++- tests/e2e/repo-wiki.test.e2e.ts | 4 +++- tests/e2e/right-settings-button.test.e2e.ts | 5 ++++- tests/e2e/utils_e2e.ts | 9 --------- 12 files changed, 27 insertions(+), 19 deletions(-) diff --git a/tests/e2e/actions.test.e2e.ts b/tests/e2e/actions.test.e2e.ts index 6236fe70d3..4e93b89ee0 100644 --- a/tests/e2e/actions.test.e2e.ts +++ b/tests/e2e/actions.test.e2e.ts @@ -71,4 +71,5 @@ test('workflow dispatch box not available for unauthenticated users', async ({pa await page.goto('/user2/test_workflows/actions?workflow=test-dispatch.yml&actor=0&status=0'); await expect(page.locator('body')).not.toContainText(workflow_trigger_notification_text); + await save_visual(page); }); diff --git a/tests/e2e/clipboard-copy.test.e2e.ts b/tests/e2e/clipboard-copy.test.e2e.ts index 70a3425868..2ae0e0dfff 100644 --- a/tests/e2e/clipboard-copy.test.e2e.ts +++ b/tests/e2e/clipboard-copy.test.e2e.ts @@ -8,7 +8,7 @@ // @watch end import {expect} from '@playwright/test'; -import {test} from './utils_e2e.ts'; +import {save_visual, test} from './utils_e2e.ts'; test('copy src file path to clipboard', async ({page}, workerInfo) => { test.skip(['Mobile Safari', 'webkit'].includes(workerInfo.project.name), 'Apple clipboard API addon - starting at just $499!'); @@ -19,6 +19,7 @@ test('copy src file path to clipboard', async ({page}, workerInfo) => { await page.click('[data-clipboard-text]'); const clipboardText = await page.evaluate(() => navigator.clipboard.readText()); expect(clipboardText).toContain('README.md'); + await save_visual(page); }); test('copy diff file path to clipboard', async ({page}, workerInfo) => { @@ -30,4 +31,6 @@ test('copy diff file path to clipboard', async ({page}, workerInfo) => { await page.click('[data-clipboard-text]'); const clipboardText = await page.evaluate(() => navigator.clipboard.readText()); expect(clipboardText).toContain('README.md'); + await expect(page.getByText('Copied')).toBeVisible(); + await save_visual(page); }); diff --git a/tests/e2e/dashboard-ci-status.test.e2e.ts b/tests/e2e/dashboard-ci-status.test.e2e.ts index 800fc951e6..d35fe299ff 100644 --- a/tests/e2e/dashboard-ci-status.test.e2e.ts +++ b/tests/e2e/dashboard-ci-status.test.e2e.ts @@ -3,7 +3,7 @@ // @watch end import {expect} from '@playwright/test'; -import {save_visual, test} from './utils_e2e.ts'; +import {test} from './utils_e2e.ts'; test.use({user: 'user2'}); @@ -23,5 +23,6 @@ test('Correct link and tooltip', async ({page}, testInfo) => { const repoStatus = page.locator('.dashboard-repos .repo-owner-name-list > li:nth-child(1) > a:nth-child(2)'); await expect(repoStatus).toHaveAttribute('href', '/user2/test_workflows/actions', {timeout: 10000}); await expect(repoStatus).toHaveAttribute('data-tooltip-content', /^(Error|Failure)$/); - await save_visual(page); + // ToDo: Ensure stable screenshot of dashboard. Known to be flaky: https://code.forgejo.org/forgejo/visual-browser-testing/commit/206d4cfb7a4af6d8d7043026cdd4d63708798b2a + // await save_visual(page); }); diff --git a/tests/e2e/example.test.e2e.ts b/tests/e2e/example.test.e2e.ts index b2a679a82d..97c5b8684b 100644 --- a/tests/e2e/example.test.e2e.ts +++ b/tests/e2e/example.test.e2e.ts @@ -5,7 +5,7 @@ // @watch end import {expect} from '@playwright/test'; -import {test} from './utils_e2e.ts'; +import {save_visual, test} from './utils_e2e.ts'; test('Load Homepage', async ({page}) => { const response = await page.goto('/'); @@ -26,6 +26,7 @@ test('Register Form', async ({page}, workerInfo) => { expect(page.url()).toBe(`${workerInfo.project.use.baseURL}/`); await expect(page.locator('.secondary-nav span>img.ui.avatar')).toBeVisible(); await expect(page.locator('.ui.positive.message.flash-success')).toHaveText('Account was successfully created. Welcome!'); + await save_visual(page); }); // eslint-disable-next-line playwright/no-skipped-test diff --git a/tests/e2e/explore.test.e2e.ts b/tests/e2e/explore.test.e2e.ts index 44c9b21f58..1bb5af3cc6 100644 --- a/tests/e2e/explore.test.e2e.ts +++ b/tests/e2e/explore.test.e2e.ts @@ -7,7 +7,7 @@ // @watch end import {expect} from '@playwright/test'; -import {test} from './utils_e2e.ts'; +import {save_visual, test} from './utils_e2e.ts'; test('Explore view taborder', async ({page}) => { await page.goto('/explore/repos'); @@ -42,4 +42,5 @@ test('Explore view taborder', async ({page}) => { } } expect(res).toBe(exp); + await save_visual(page); }); diff --git a/tests/e2e/markup.test.e2e.ts b/tests/e2e/markup.test.e2e.ts index 2726942d57..398a0a6300 100644 --- a/tests/e2e/markup.test.e2e.ts +++ b/tests/e2e/markup.test.e2e.ts @@ -3,7 +3,7 @@ // @watch end import {expect} from '@playwright/test'; -import {test} from './utils_e2e.ts'; +import {save_visual, test} from './utils_e2e.ts'; test('markup with #xyz-mode-only', async ({page}) => { const response = await page.goto('/user2/repo1/issues/1'); @@ -13,4 +13,5 @@ test('markup with #xyz-mode-only', async ({page}) => { await expect(comment).toBeVisible(); await expect(comment.locator('[src$="#gh-light-mode-only"]')).toBeVisible(); await expect(comment.locator('[src$="#gh-dark-mode-only"]')).toBeHidden(); + await save_visual(page); }); diff --git a/tests/e2e/repo-code.test.e2e.ts b/tests/e2e/repo-code.test.e2e.ts index 335fb5b7f5..11b710c956 100644 --- a/tests/e2e/repo-code.test.e2e.ts +++ b/tests/e2e/repo-code.test.e2e.ts @@ -49,6 +49,7 @@ test('Line Range Selection', async ({page}) => { // out-of-bounds end line await page.goto(`${filePath}#L1-L100`); await assertSelectedLines(page, ['1', '2', '3']); + await save_visual(page); }); test('Readable diff', async ({page}, workerInfo) => { @@ -75,6 +76,7 @@ test('Readable diff', async ({page}, workerInfo) => { await expect(page.getByText(thisDiff.added, {exact: true})).toHaveCSS('background-color', 'rgb(134, 239, 172)'); } } + await save_visual(page); }); test.describe('As authenticated user', () => { diff --git a/tests/e2e/repo-commitgraph.test.e2e.ts b/tests/e2e/repo-commitgraph.test.e2e.ts index 5f0cad117a..39c5661900 100644 --- a/tests/e2e/repo-commitgraph.test.e2e.ts +++ b/tests/e2e/repo-commitgraph.test.e2e.ts @@ -5,7 +5,7 @@ // @watch end import {expect} from '@playwright/test'; -import {test} from './utils_e2e.ts'; +import {save_visual, test} from './utils_e2e.ts'; test('Commit graph overflow', async ({page}) => { await page.goto('/user2/diff-test/graph'); @@ -28,4 +28,5 @@ test('Switch branch', async ({page}) => { await expect(page.locator('#loading-indicator')).toBeHidden(); await expect(page.locator('#rel-container')).toBeVisible(); await expect(page.locator('#rev-container')).toBeVisible(); + await save_visual(page); }); diff --git a/tests/e2e/repo-migrate.test.e2e.ts b/tests/e2e/repo-migrate.test.e2e.ts index 428c2cb171..5e67f89ed1 100644 --- a/tests/e2e/repo-migrate.test.e2e.ts +++ b/tests/e2e/repo-migrate.test.e2e.ts @@ -21,7 +21,6 @@ test('Migration Progress Page', async ({page, browser}, workerInfo) => { await form.locator('button.primary').click({timeout: 5000}); await expect(page).toHaveURL('user2/invalidrepo'); await save_visual(page); - // page screenshot of unauthenticatedPage is checked automatically after the test const ctx = await test_context(browser); const unauthenticatedPage = await ctx.newPage(); @@ -37,4 +36,6 @@ test('Migration Progress Page', async ({page, browser}, workerInfo) => { await save_visual(page); await deleteModal.getByRole('button', {name: 'Delete repository'}).click(); await expect(page).toHaveURL('/'); + // checked last to preserve the order of screenshots from first run + await save_visual(unauthenticatedPage); }); diff --git a/tests/e2e/repo-wiki.test.e2e.ts b/tests/e2e/repo-wiki.test.e2e.ts index f32fe3fc91..4ce66da8bc 100644 --- a/tests/e2e/repo-wiki.test.e2e.ts +++ b/tests/e2e/repo-wiki.test.e2e.ts @@ -4,7 +4,7 @@ // @watch end import {expect} from '@playwright/test'; -import {test} from './utils_e2e.ts'; +import {save_visual, test} from './utils_e2e.ts'; for (const searchTerm of ['space', 'consectetur']) { for (const width of [null, 2560, 4000]) { @@ -23,6 +23,7 @@ for (const searchTerm of ['space', 'consectetur']) { await page.getByPlaceholder('Search wiki').dispatchEvent('keyup'); // timeout is necessary because HTMX search could be slow await expect(page.locator('#wiki-search a[href]')).toBeInViewport({ratio: 1}); + await save_visual(page); }); } } @@ -36,4 +37,5 @@ test(`Search results show titles (and not file names)`, async ({page}, workerInf // so we manually "type" the last letter await page.getByPlaceholder('Search wiki').dispatchEvent('keyup'); await expect(page.locator('#wiki-search a[href] b')).toHaveText('Page With Spaced Name'); + await save_visual(page); }); diff --git a/tests/e2e/right-settings-button.test.e2e.ts b/tests/e2e/right-settings-button.test.e2e.ts index e1c40fdd4d..3bea329ba0 100644 --- a/tests/e2e/right-settings-button.test.e2e.ts +++ b/tests/e2e/right-settings-button.test.e2e.ts @@ -5,7 +5,7 @@ // @watch end import {expect} from '@playwright/test'; -import {test} from './utils_e2e.ts'; +import {save_visual, test} from './utils_e2e.ts'; test.describe('desktop viewport as user 2', () => { test.use({user: 'user2', viewport: {width: 1920, height: 300}}); @@ -54,6 +54,7 @@ test.describe('desktop viewport, unauthenticated', () => { await expect(page.locator('.overflow-menu-items>#settings-btn')).toHaveCount(0); await expect(page.locator('.overflow-menu-button')).toHaveCount(0); + await save_visual(page); }); }); @@ -78,6 +79,7 @@ test.describe('small viewport', () => { const items = shownItems.concat(overflowItems); expect(Array.from(new Set(items))).toHaveLength(items.length); + await save_visual(page); }); test('Settings button in overflow menu of org header', async ({page}) => { @@ -121,5 +123,6 @@ test.describe('small viewport, unauthenticated', () => { const items = shownItems.concat(overflowItems); expect(Array.from(new Set(items))).toHaveLength(items.length); + await save_visual(page); }); }); diff --git a/tests/e2e/utils_e2e.ts b/tests/e2e/utils_e2e.ts index 80412e437d..18d23753dd 100644 --- a/tests/e2e/utils_e2e.ts +++ b/tests/e2e/utils_e2e.ts @@ -26,15 +26,6 @@ export const test = baseTest.extend({ }, user: null, authScope: 'shared', - // see https://playwright.dev/docs/test-fixtures#adding-global-beforeeachaftereach-hooks - forEachTest: [async ({page}, use) => { - await use(); - // some tests create a new page which is not yet available here - // only operate on tests that make the URL available - if (page.url() !== 'about:blank') { - await save_visual(page); - } - }, {auto: true}], }); export async function test_context(browser: Browser, options?: BrowserContextOptions) { From f7ba8f0e413db23006e72af7e78d5b9ec2f36413 Mon Sep 17 00:00:00 2001 From: Otto Richter Date: Tue, 14 Jan 2025 22:28:13 +0100 Subject: [PATCH 05/23] tests(e2e): Do not fail early when comparing screenshots Also set a unique outputDir so that artifacts are preserved and not overwritten by the following tests. --- tests/e2e/e2e_test.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/e2e/e2e_test.go b/tests/e2e/e2e_test.go index 88b254b6f9..8e4a9653cd 100644 --- a/tests/e2e/e2e_test.go +++ b/tests/e2e/e2e_test.go @@ -82,6 +82,7 @@ func TestE2e(t *testing.T) { runArgs := []string{"npx", "playwright", "test"} + _, testVisual := os.LookupEnv("VISUAL_TEST") // To update snapshot outputs if _, set := os.LookupEnv("ACCEPT_VISUAL"); set { runArgs = append(runArgs, "--update-snapshots") @@ -105,6 +106,10 @@ func TestE2e(t *testing.T) { onForgejoRun(t, func(*testing.T, *url.URL) { defer DeclareGitRepos(t)() thisTest := runArgs + // when all tests are run, use unique artifacts directories per test to preserve artifacts from other tests + if testVisual { + thisTest = append(thisTest, "--output=tests/e2e/test-artifacts/"+testname) + } thisTest = append(thisTest, path) cmd := exec.Command(runArgs[0], thisTest...) cmd.Env = os.Environ() @@ -114,7 +119,7 @@ func TestE2e(t *testing.T) { cmd.Stderr = os.Stderr err := cmd.Run() - if err != nil { + if err != nil && !testVisual { log.Fatal("Playwright Failed: %s", err) } }) From e7299eb0fb0e75601e264977ba391e80c1924b2c Mon Sep 17 00:00:00 2001 From: Otto Richter Date: Wed, 15 Jan 2025 15:30:41 +0100 Subject: [PATCH 06/23] tests(e2e): Mask user heatmap in screenshots It depends on both relative activity and date the test is run on --- tests/e2e/utils_e2e.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/e2e/utils_e2e.ts b/tests/e2e/utils_e2e.ts index 18d23753dd..ff921a2cf3 100644 --- a/tests/e2e/utils_e2e.ts +++ b/tests/e2e/utils_e2e.ts @@ -119,6 +119,7 @@ export async function save_visual(page: Page) { // update order of recently created repos is not fully deterministic page.locator('.flex-item-main').filter({hasText: 'relative time in repo'}), page.locator('#activity-feed'), + page.locator('#user-heatmap'), // dynamic IDs in fixed-size inputs page.locator('input[value*="dyn-id-"]'), ], From 909738e6f60c62b4cc0d5558bb57dc22f705e619 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Thu, 16 Jan 2025 15:20:57 +0000 Subject: [PATCH 07/23] Update renovate Docker tag to v39.111.0 (forgejo) (#6570) Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/6570 Reviewed-by: Michael Kriese Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 3f05ae0b93..039004603c 100644 --- a/Makefile +++ b/Makefile @@ -49,7 +49,7 @@ GOVULNCHECK_PACKAGE ?= golang.org/x/vuln/cmd/govulncheck@v1 # renovate: datasour DEADCODE_PACKAGE ?= golang.org/x/tools/cmd/deadcode@v0.29.0 # renovate: datasource=go GOMOCK_PACKAGE ?= go.uber.org/mock/mockgen@v0.4.0 # renovate: datasource=go GOPLS_PACKAGE ?= golang.org/x/tools/gopls@v0.17.1 # renovate: datasource=go -RENOVATE_NPM_PACKAGE ?= renovate@39.106.0 # renovate: datasource=docker packageName=data.forgejo.org/renovate/renovate +RENOVATE_NPM_PACKAGE ?= renovate@39.111.0 # renovate: datasource=docker packageName=data.forgejo.org/renovate/renovate # https://github.com/disposable-email-domains/disposable-email-domains/commits/main/ DISPOSABLE_EMAILS_SHA ?= 0c27e671231d27cf66370034d7f6818037416989 # renovate: ... From 907ab8bdef4237bd9240b3fd36c884b4b2375499 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Thu, 16 Jan 2025 22:51:49 +0000 Subject: [PATCH 08/23] Update dependency @github/relative-time-element to v4.4.5 (forgejo) (#6559) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | [@github/relative-time-element](https://github.com/github/relative-time-element) | dependencies | patch | [`4.4.4` -> `4.4.5`](https://renovatebot.com/diffs/npm/@github%2frelative-time-element/4.4.4/4.4.5) | --- ### Release Notes
github/relative-time-element (@​github/relative-time-element) ### [`v4.4.5`](https://github.com/github/relative-time-element/releases/tag/v4.4.5) [Compare Source](https://github.com/github/relative-time-element/compare/v4.4.4...v4.4.5) #### What's Changed - fix: wrap Intl.<>() calls in try/catch by [@​francinelucca](https://github.com/francinelucca) in https://github.com/github/relative-time-element/pull/297 - get main branch green by [@​keithamus](https://github.com/keithamus) in https://github.com/github/relative-time-element/pull/302 - Make `applyDuration` reversible by [@​leduyquang753](https://github.com/leduyquang753) in https://github.com/github/relative-time-element/pull/298 - Use node v22 by [@​camertron](https://github.com/camertron) in https://github.com/github/relative-time-element/pull/303 #### New Contributors - [@​francinelucca](https://github.com/francinelucca) made their first contribution in https://github.com/github/relative-time-element/pull/297 - [@​leduyquang753](https://github.com/leduyquang753) made their first contribution in https://github.com/github/relative-time-element/pull/298 - [@​camertron](https://github.com/camertron) made their first contribution in https://github.com/github/relative-time-element/pull/303 **Full Changelog**: https://github.com/github/relative-time-element/compare/v4.4.4...v4.4.5
--- ### Configuration 📅 **Schedule**: Branch creation - "* 0-3 * * *" (UTC), Automerge - "* 0-3 * * *" (UTC). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/6559 Reviewed-by: Otto Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 223dfbd310..9dbfaf2551 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "@citation-js/plugin-software-formats": "0.6.1", "@github/markdown-toolbar-element": "2.2.3", "@github/quote-selection": "2.1.0", - "@github/relative-time-element": "4.4.4", + "@github/relative-time-element": "4.4.5", "@github/text-expander-element": "2.8.0", "@mcaptcha/vanilla-glue": "0.1.0-alpha-3", "@primer/octicons": "19.14.0", @@ -2854,9 +2854,9 @@ "license": "MIT" }, "node_modules/@github/relative-time-element": { - "version": "4.4.4", - "resolved": "https://registry.npmjs.org/@github/relative-time-element/-/relative-time-element-4.4.4.tgz", - "integrity": "sha512-Oi8uOL8O+ZWLD7dHRWCkm2cudcTYtB3VyOYf9BtzCgDGm+OKomyOREtItNMtWl1dxvec62BTKErq36uy+RYxQg==", + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/@github/relative-time-element/-/relative-time-element-4.4.5.tgz", + "integrity": "sha512-9ejPtayBDIJfEU8x1fg/w2o5mahHkkp1SC6uObDtoKs4Gn+2a1vNK8XIiNDD8rMeEfpvDjydgSZZ+uk+7N0VsQ==", "license": "MIT" }, "node_modules/@github/text-expander-element": { diff --git a/package.json b/package.json index 86b1b17029..944789f0a7 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "@citation-js/plugin-software-formats": "0.6.1", "@github/markdown-toolbar-element": "2.2.3", "@github/quote-selection": "2.1.0", - "@github/relative-time-element": "4.4.4", + "@github/relative-time-element": "4.4.5", "@github/text-expander-element": "2.8.0", "@mcaptcha/vanilla-glue": "0.1.0-alpha-3", "@primer/octicons": "19.14.0", From cefd786685b8b6f56cc26cb84019376a5a281be3 Mon Sep 17 00:00:00 2001 From: Robert Wolff Date: Thu, 16 Jan 2025 23:59:21 +0000 Subject: [PATCH 09/23] Remove source branch from pr list, fix #5009, #6080 (#6522) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ### What? It removes the source branch that is not necessary in the PR list (see #5009). It adds a little chevron to the right in front of the target branch. That could be replaced by words (“into”), or removed, if preferred. But, I think it looks decent like that. ### Screenshots #### Before ![image](/attachments/be3c9817-2207-4610-b6bd-70304436f81d) #### After ![image](/attachments/a7c84d2f-1592-4a82-aecc-d038f9495ef7) ### Testing Run the development version of forgejo from the PR. For any existing repository with PRs, open the pulls list. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/6522 Reviewed-by: Otto Reviewed-by: Gusted Co-authored-by: Robert Wolff Co-committed-by: Robert Wolff --- templates/shared/issuelist.tmpl | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/templates/shared/issuelist.tmpl b/templates/shared/issuelist.tmpl index 7ce7cd6795..b71fde685e 100644 --- a/templates/shared/issuelist.tmpl +++ b/templates/shared/issuelist.tmpl @@ -70,21 +70,13 @@ {{end}} {{if .IsPull}} {{end}} {{if and .Milestone (ne $.listType "milestone")}} From b2a3a0411c7e26d4805e6846e9413299e999b636 Mon Sep 17 00:00:00 2001 From: Gusted Date: Fri, 17 Jan 2025 03:17:10 +0000 Subject: [PATCH 10/23] [PORT] Remove SHA1 for support for ssh rsa signing (#31857) (#5303) https://github.com/go-fed/httpsig seems to be unmaintained. Switch to github.com/42wim/httpsig which has removed deprecated crypto and default sha256 signing for ssh rsa. No impact for those that use ed25519 ssh certificates. This is a breaking change for: - gitea.com/gitea/tea (go-sdk) - I'll be sending a PR there too - activitypub using deprecated crypto (is this actually used?) (cherry picked from commit 01dec7577a051d9bb30e91f6cf6653dc51a37d06) --- Conflict resolution: trivial Co-authored-by: Wim Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/5303 Reviewed-by: Earl Warren --- go.mod | 4 ++-- go.sum | 4 ++-- modules/activitypub/client.go | 2 +- modules/setting/federation.go | 2 +- routers/api/v1/activitypub/reqsignature.go | 2 +- services/auth/httpsign.go | 4 ++-- tests/integration/api_httpsig_test.go | 2 +- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/go.mod b/go.mod index e069a33996..48e9e49075 100644 --- a/go.mod +++ b/go.mod @@ -17,6 +17,7 @@ require ( codeberg.org/gusted/mcaptcha v0.0.0-20220723083913-4f3072e1d570 connectrpc.com/connect v1.17.0 gitea.com/lunny/levelqueue v0.4.2-0.20230414023320-3c0159fe0fe4 + github.com/42wim/httpsig v1.2.2 github.com/42wim/sshsig v0.0.0-20211121163825-841cf5bbc121 github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 github.com/ProtonMail/go-crypto v1.1.4 @@ -43,7 +44,6 @@ require ( github.com/go-chi/cors v1.2.1 github.com/go-co-op/gocron v1.37.0 github.com/go-enry/go-enry/v2 v2.9.1 - github.com/go-fed/httpsig v1.1.1-0.20201223112313-55836744818e github.com/go-git/go-git/v5 v5.13.1 github.com/go-ldap/ldap/v3 v3.4.6 github.com/go-openapi/spec v0.20.14 @@ -131,7 +131,6 @@ require ( dario.cat/mergo v1.0.0 // indirect filippo.io/edwards25519 v1.1.0 // indirect git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078 // indirect - github.com/42wim/httpsig v1.2.2 // indirect github.com/DataDog/zstd v1.5.5 // indirect github.com/GoogleCloudPlatform/grpc-gcp-go/grpcgcp v1.5.0 // indirect github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.24.2 // indirect @@ -184,6 +183,7 @@ require ( github.com/go-ap/errors v0.0.0-20231003111023-183eef4b31b7 // indirect github.com/go-asn1-ber/asn1-ber v1.5.5 // indirect github.com/go-enry/go-oniguruma v1.2.1 // indirect + github.com/go-fed/httpsig v1.1.0 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect github.com/go-git/go-billy/v5 v5.6.1 // indirect github.com/go-ini/ini v1.67.0 // indirect diff --git a/go.sum b/go.sum index 58abf76d25..28e1f22870 100644 --- a/go.sum +++ b/go.sum @@ -919,8 +919,8 @@ github.com/go-faster/city v1.0.1 h1:4WAxSZ3V2Ws4QRDrscLEDcibJY8uf41H6AhXDrNDcGw= github.com/go-faster/city v1.0.1/go.mod h1:jKcUJId49qdW3L1qKHH/3wPeUstCVpVSXTM6vO3VcTw= github.com/go-faster/errors v0.7.1 h1:MkJTnDoEdi9pDabt1dpWf7AA8/BaSYZqibYyhZ20AYg= github.com/go-faster/errors v0.7.1/go.mod h1:5ySTjWFiphBs07IKuiL69nxdfd5+fzh1u7FPGZP2quo= -github.com/go-fed/httpsig v1.1.1-0.20201223112313-55836744818e h1:oRq/fiirun5HqlEWMLIcDmLpIELlG4iGbd0s8iqgPi8= -github.com/go-fed/httpsig v1.1.1-0.20201223112313-55836744818e/go.mod h1:RCMrTZvN1bJYtofsG4rd5NaO5obxQ5xBkdiS7xsT7bM= +github.com/go-fed/httpsig v1.1.0 h1:9M+hb0jkEICD8/cAiNqEB66R87tTINszBRTjwjQzWcI= +github.com/go-fed/httpsig v1.1.0/go.mod h1:RCMrTZvN1bJYtofsG4rd5NaO5obxQ5xBkdiS7xsT7bM= github.com/go-fonts/dejavu v0.1.0/go.mod h1:4Wt4I4OU2Nq9asgDCteaAaWZOV24E+0/Pwo0gppep4g= github.com/go-fonts/latin-modern v0.2.0/go.mod h1:rQVLdDMK+mK1xscDwsqM5J8U2jrRa3T0ecnM9pNujks= github.com/go-fonts/liberation v0.1.1/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY= diff --git a/modules/activitypub/client.go b/modules/activitypub/client.go index 064d8984c1..645a4b8c8d 100644 --- a/modules/activitypub/client.go +++ b/modules/activitypub/client.go @@ -22,7 +22,7 @@ import ( "code.gitea.io/gitea/modules/proxy" "code.gitea.io/gitea/modules/setting" - "github.com/go-fed/httpsig" + "github.com/42wim/httpsig" ) const ( diff --git a/modules/setting/federation.go b/modules/setting/federation.go index aeb30683ea..edb18e0054 100644 --- a/modules/setting/federation.go +++ b/modules/setting/federation.go @@ -6,7 +6,7 @@ package setting import ( "code.gitea.io/gitea/modules/log" - "github.com/go-fed/httpsig" + "github.com/42wim/httpsig" ) // Federation settings diff --git a/routers/api/v1/activitypub/reqsignature.go b/routers/api/v1/activitypub/reqsignature.go index 6003f664a0..19d167b50b 100644 --- a/routers/api/v1/activitypub/reqsignature.go +++ b/routers/api/v1/activitypub/reqsignature.go @@ -18,8 +18,8 @@ import ( "code.gitea.io/gitea/modules/setting" gitea_context "code.gitea.io/gitea/services/context" + "github.com/42wim/httpsig" ap "github.com/go-ap/activitypub" - "github.com/go-fed/httpsig" ) func getPublicKeyFromResponse(b []byte, keyID *url.URL) (p crypto.PublicKey, err error) { diff --git a/services/auth/httpsign.go b/services/auth/httpsign.go index b604349f80..83a36bef23 100644 --- a/services/auth/httpsign.go +++ b/services/auth/httpsign.go @@ -17,7 +17,7 @@ import ( "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" - "github.com/go-fed/httpsig" + "github.com/42wim/httpsig" "golang.org/x/crypto/ssh" ) @@ -205,7 +205,7 @@ func doVerify(verifier httpsig.Verifier, sshPublicKeys []ssh.PublicKey) error { case strings.HasPrefix(publicKey.Type(), "ssh-ed25519"): algos = []httpsig.Algorithm{httpsig.ED25519} case strings.HasPrefix(publicKey.Type(), "ssh-rsa"): - algos = []httpsig.Algorithm{httpsig.RSA_SHA1, httpsig.RSA_SHA256, httpsig.RSA_SHA512} + algos = []httpsig.Algorithm{httpsig.RSA_SHA256, httpsig.RSA_SHA512} } for _, algo := range algos { if err := verifier.Verify(cryptoPubkey, algo); err == nil { diff --git a/tests/integration/api_httpsig_test.go b/tests/integration/api_httpsig_test.go index 30aed3cacc..cca477f5e1 100644 --- a/tests/integration/api_httpsig_test.go +++ b/tests/integration/api_httpsig_test.go @@ -15,7 +15,7 @@ import ( "code.gitea.io/gitea/modules/test" "code.gitea.io/gitea/tests" - "github.com/go-fed/httpsig" + "github.com/42wim/httpsig" "golang.org/x/crypto/ssh" ) From 376a2e19eab62640257b40db8163a0cab92c7038 Mon Sep 17 00:00:00 2001 From: Earl Warren Date: Fri, 17 Jan 2025 07:42:20 +0000 Subject: [PATCH 11/23] fix: reduce noise for the v303 migration (#6591) Using SELECT `%s` FROM `%s` WHERE 0 = 1 to assert the existence of a column is simple but noisy: it shows errors in the migrations that are confusing for Forgejo admins because they are not actual errors. Use introspection instead, which is more complicated but leads to the same result. Add a test that ensures it works as expected, for all database types. Although the migration is run for all database types, it does not account for various scenarios and is never tested in the case a column does not exist. Refs: https://codeberg.org/forgejo/forgejo/issues/6583 Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/6591 Reviewed-by: Otto Co-authored-by: Earl Warren Co-committed-by: Earl Warren --- models/migrations/v1_23/v303.go | 35 ++++++++++++++++++------ models/migrations/v1_23/v303_test.go | 41 ++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+), 8 deletions(-) create mode 100644 models/migrations/v1_23/v303_test.go diff --git a/models/migrations/v1_23/v303.go b/models/migrations/v1_23/v303.go index e3ee180539..2fb37ac843 100644 --- a/models/migrations/v1_23/v303.go +++ b/models/migrations/v1_23/v303.go @@ -1,23 +1,27 @@ -// Copyright 2024 The Forgejo Authors. -// SPDX-License-Identifier: MIT +// Copyright 2025 The Forgejo Authors. +// SPDX-License-Identifier: GPL-3.0-or-later package v1_23 //nolint import ( - "fmt" - "code.gitea.io/gitea/models/migrations/base" "xorm.io/xorm" + "xorm.io/xorm/schemas" ) func GiteaLastDrop(x *xorm.Engine) error { + tables, err := x.DBMetas() + if err != nil { + return err + } + sess := x.NewSession() defer sess.Close() for _, drop := range []struct { - table string - field string + table string + column string }{ {"badge", "slug"}, {"oauth2_application", "skip_secondary_authorization"}, @@ -29,10 +33,25 @@ func GiteaLastDrop(x *xorm.Engine) error { {"protected_branch", "force_push_allowlist_team_i_ds"}, {"protected_branch", "force_push_allowlist_deploy_keys"}, } { - if _, err := sess.Exec(fmt.Sprintf("SELECT `%s` FROM `%s` WHERE 0 = 1", drop.field, drop.table)); err != nil { + var table *schemas.Table + found := false + + for _, table = range tables { + if table.Name == drop.table { + found = true + break + } + } + + if !found { continue } - if err := base.DropTableColumns(sess, drop.table, drop.field); err != nil { + + if table.GetColumn(drop.column) == nil { + continue + } + + if err := base.DropTableColumns(sess, drop.table, drop.column); err != nil { return err } } diff --git a/models/migrations/v1_23/v303_test.go b/models/migrations/v1_23/v303_test.go new file mode 100644 index 0000000000..752eacee0c --- /dev/null +++ b/models/migrations/v1_23/v303_test.go @@ -0,0 +1,41 @@ +// Copyright 2025 The Forgejo Authors. +// SPDX-License-Identifier: GPL-3.0-or-later + +package v1_23 //nolint + +import ( + "testing" + + migration_tests "code.gitea.io/gitea/models/migrations/test" + + "github.com/stretchr/testify/require" + "xorm.io/xorm/schemas" +) + +func Test_GiteaLastDrop(t *testing.T) { + type Badge struct { + ID int64 `xorm:"pk autoincr"` + Slug string + } + + x, deferable := migration_tests.PrepareTestEnv(t, 0, new(Badge)) + defer deferable() + if x == nil || t.Failed() { + return + } + + getColumn := func() *schemas.Column { + tables, err := x.DBMetas() + require.NoError(t, err) + require.Len(t, tables, 1) + table := tables[0] + require.Equal(t, "badge", table.Name) + return table.GetColumn("slug") + } + + require.NotNil(t, getColumn(), "slug column exists") + require.NoError(t, GiteaLastDrop(x)) + require.Nil(t, getColumn(), "slug column was deleted") + // idempotent + require.NoError(t, GiteaLastDrop(x)) +} From a2787bb09edcf00f29a65c4f405de6371b4d434e Mon Sep 17 00:00:00 2001 From: Benedikt Straub Date: Mon, 30 Dec 2024 20:03:37 +0100 Subject: [PATCH 12/23] Initial support for localization and pluralization with go-i18n-JSON-v2 format --- .deadcode-out | 1 + modules/translation/i18n/dummy.go | 20 +- modules/translation/i18n/errors.go | 6 +- modules/translation/i18n/i18n.go | 23 ++- modules/translation/i18n/i18n_test.go | 106 +++++++++- modules/translation/i18n/localestore.go | 180 +++++++++++++++-- modules/translation/mock.go | 4 + modules/translation/plural_rules.go | 253 ++++++++++++++++++++++++ modules/translation/translation.go | 16 +- modules/translation/translation_test.go | 108 ++++++++++ options/locale/locale_en-US.ini | 5 - options/locale_next/locale_ar.json | 1 + options/locale_next/locale_be.json | 1 + options/locale_next/locale_bg.json | 14 ++ options/locale_next/locale_bn.json | 1 + options/locale_next/locale_bs.json | 1 + options/locale_next/locale_ca.json | 5 + options/locale_next/locale_cs-CZ.json | 17 ++ options/locale_next/locale_da.json | 5 + options/locale_next/locale_de-DE.json | 17 ++ options/locale_next/locale_el-GR.json | 17 ++ options/locale_next/locale_en-US.json | 17 ++ options/locale_next/locale_eo.json | 5 + options/locale_next/locale_es-ES.json | 17 ++ options/locale_next/locale_et.json | 5 + options/locale_next/locale_fa-IR.json | 12 ++ options/locale_next/locale_fi-FI.json | 15 ++ options/locale_next/locale_fil.json | 17 ++ options/locale_next/locale_fr-FR.json | 17 ++ options/locale_next/locale_gl.json | 1 + options/locale_next/locale_hi.json | 1 + options/locale_next/locale_hu-HU.json | 15 ++ options/locale_next/locale_id-ID.json | 12 ++ options/locale_next/locale_is-IS.json | 9 + options/locale_next/locale_it-IT.json | 17 ++ options/locale_next/locale_ja-JP.json | 15 ++ options/locale_next/locale_ko-KR.json | 12 ++ options/locale_next/locale_lt.json | 5 + options/locale_next/locale_lv-LV.json | 17 ++ options/locale_next/locale_ml-IN.json | 1 + options/locale_next/locale_nb_NO.json | 1 + options/locale_next/locale_nds.json | 17 ++ options/locale_next/locale_nl-NL.json | 17 ++ options/locale_next/locale_pl-PL.json | 15 ++ options/locale_next/locale_pt-BR.json | 17 ++ options/locale_next/locale_pt-PT.json | 17 ++ options/locale_next/locale_ru-RU.json | 17 ++ options/locale_next/locale_si-LK.json | 12 ++ options/locale_next/locale_sk-SK.json | 1 + options/locale_next/locale_sl.json | 1 + options/locale_next/locale_sr-SP.json | 1 + options/locale_next/locale_sv-SE.json | 15 ++ options/locale_next/locale_tr-TR.json | 15 ++ options/locale_next/locale_uk-UA.json | 17 ++ options/locale_next/locale_vi.json | 1 + options/locale_next/locale_yi.json | 1 + options/locale_next/locale_zh-CN.json | 15 ++ options/locale_next/locale_zh-HK.json | 9 + options/locale_next/locale_zh-TW.json | 15 ++ templates/repo/issue/view_title.tmpl | 8 +- tools/migrate_locales.sh | 145 ++++++++++++++ 61 files changed, 1317 insertions(+), 51 deletions(-) create mode 100644 modules/translation/plural_rules.go create mode 100644 options/locale_next/locale_ar.json create mode 100644 options/locale_next/locale_be.json create mode 100644 options/locale_next/locale_bg.json create mode 100644 options/locale_next/locale_bn.json create mode 100644 options/locale_next/locale_bs.json create mode 100644 options/locale_next/locale_ca.json create mode 100644 options/locale_next/locale_cs-CZ.json create mode 100644 options/locale_next/locale_da.json create mode 100644 options/locale_next/locale_de-DE.json create mode 100644 options/locale_next/locale_el-GR.json create mode 100644 options/locale_next/locale_en-US.json create mode 100644 options/locale_next/locale_eo.json create mode 100644 options/locale_next/locale_es-ES.json create mode 100644 options/locale_next/locale_et.json create mode 100644 options/locale_next/locale_fa-IR.json create mode 100644 options/locale_next/locale_fi-FI.json create mode 100644 options/locale_next/locale_fil.json create mode 100644 options/locale_next/locale_fr-FR.json create mode 100644 options/locale_next/locale_gl.json create mode 100644 options/locale_next/locale_hi.json create mode 100644 options/locale_next/locale_hu-HU.json create mode 100644 options/locale_next/locale_id-ID.json create mode 100644 options/locale_next/locale_is-IS.json create mode 100644 options/locale_next/locale_it-IT.json create mode 100644 options/locale_next/locale_ja-JP.json create mode 100644 options/locale_next/locale_ko-KR.json create mode 100644 options/locale_next/locale_lt.json create mode 100644 options/locale_next/locale_lv-LV.json create mode 100644 options/locale_next/locale_ml-IN.json create mode 100644 options/locale_next/locale_nb_NO.json create mode 100644 options/locale_next/locale_nds.json create mode 100644 options/locale_next/locale_nl-NL.json create mode 100644 options/locale_next/locale_pl-PL.json create mode 100644 options/locale_next/locale_pt-BR.json create mode 100644 options/locale_next/locale_pt-PT.json create mode 100644 options/locale_next/locale_ru-RU.json create mode 100644 options/locale_next/locale_si-LK.json create mode 100644 options/locale_next/locale_sk-SK.json create mode 100644 options/locale_next/locale_sl.json create mode 100644 options/locale_next/locale_sr-SP.json create mode 100644 options/locale_next/locale_sv-SE.json create mode 100644 options/locale_next/locale_tr-TR.json create mode 100644 options/locale_next/locale_uk-UA.json create mode 100644 options/locale_next/locale_vi.json create mode 100644 options/locale_next/locale_yi.json create mode 100644 options/locale_next/locale_zh-CN.json create mode 100644 options/locale_next/locale_zh-HK.json create mode 100644 options/locale_next/locale_zh-TW.json create mode 100755 tools/migrate_locales.sh diff --git a/.deadcode-out b/.deadcode-out index 64741ec7ac..a44599b6f1 100644 --- a/.deadcode-out +++ b/.deadcode-out @@ -246,6 +246,7 @@ code.gitea.io/gitea/modules/translation MockLocale.TrString MockLocale.Tr MockLocale.TrN + MockLocale.TrPluralString MockLocale.TrSize MockLocale.PrettyNumber diff --git a/modules/translation/i18n/dummy.go b/modules/translation/i18n/dummy.go index fe15c250f4..861672c619 100644 --- a/modules/translation/i18n/dummy.go +++ b/modules/translation/i18n/dummy.go @@ -22,20 +22,7 @@ func (k *KeyLocale) HasKey(trKey string) bool { // TrHTML implements Locale. func (k *KeyLocale) TrHTML(trKey string, trArgs ...any) template.HTML { - args := slices.Clone(trArgs) - for i, v := range args { - switch v := v.(type) { - case nil, bool, int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float32, float64, template.HTML: - // for most basic types (including template.HTML which is safe), just do nothing and use it - case string: - args[i] = template.HTMLEscapeString(v) - case fmt.Stringer: - args[i] = template.HTMLEscapeString(v.String()) - default: - args[i] = template.HTMLEscapeString(fmt.Sprint(v)) - } - } - return template.HTML(k.TrString(trKey, args...)) + return template.HTML(k.TrString(trKey, PrepareArgsForHTML(trArgs...)...)) } // TrString implements Locale. @@ -43,6 +30,11 @@ func (k *KeyLocale) TrString(trKey string, trArgs ...any) string { return FormatDummy(trKey, trArgs...) } +// TrPluralString implements Locale. +func (k *KeyLocale) TrPluralString(count any, trKey string, trArgs ...any) template.HTML { + return template.HTML(FormatDummy(trKey, PrepareArgsForHTML(trArgs...)...)) +} + func FormatDummy(trKey string, args ...any) string { if len(args) == 0 { return fmt.Sprintf("(%s)", trKey) diff --git a/modules/translation/i18n/errors.go b/modules/translation/i18n/errors.go index 7f64ccf908..ee9436a8f7 100644 --- a/modules/translation/i18n/errors.go +++ b/modules/translation/i18n/errors.go @@ -8,6 +8,8 @@ import ( ) var ( - ErrLocaleAlreadyExist = util.SilentWrap{Message: "lang already exists", Err: util.ErrAlreadyExist} - ErrUncertainArguments = util.SilentWrap{Message: "arguments to i18n should not contain uncertain slices", Err: util.ErrInvalidArgument} + ErrLocaleAlreadyExist = util.SilentWrap{Message: "lang already exists", Err: util.ErrAlreadyExist} + ErrLocaleDoesNotExist = util.SilentWrap{Message: "lang does not exist", Err: util.ErrNotExist} + ErrTranslationDoesNotExist = util.SilentWrap{Message: "translation does not exist", Err: util.ErrNotExist} + ErrUncertainArguments = util.SilentWrap{Message: "arguments to i18n should not contain uncertain slices", Err: util.ErrInvalidArgument} ) diff --git a/modules/translation/i18n/i18n.go b/modules/translation/i18n/i18n.go index 1555cd961e..e447502a3b 100644 --- a/modules/translation/i18n/i18n.go +++ b/modules/translation/i18n/i18n.go @@ -8,11 +8,28 @@ import ( "io" ) +type ( + PluralFormIndex uint8 + PluralFormRule func(int64) PluralFormIndex +) + +const ( + PluralFormZero PluralFormIndex = iota + PluralFormOne + PluralFormTwo + PluralFormFew + PluralFormMany + PluralFormOther +) + var DefaultLocales = NewLocaleStore() type Locale interface { // TrString translates a given key and arguments for a language TrString(trKey string, trArgs ...any) string + // TrPluralString translates a given pluralized key and arguments for a language. + // This function returns an error if new-style support for the given key is not available. + TrPluralString(count any, trKey string, trArgs ...any) template.HTML // TrHTML translates a given key and arguments for a language, string arguments are escaped to HTML TrHTML(trKey string, trArgs ...any) template.HTML // HasKey reports if a locale has a translation for a given key @@ -31,8 +48,10 @@ type LocaleStore interface { Locale(langName string) (Locale, bool) // HasLang returns whether a given language is present in the store HasLang(langName string) bool - // AddLocaleByIni adds a new language to the store - AddLocaleByIni(langName, langDesc string, source, moreSource []byte) error + // AddLocaleByIni adds a new old-style language to the store + AddLocaleByIni(langName, langDesc string, pluralRule PluralFormRule, source, moreSource []byte) error + // AddLocaleByJSON adds new-style content to an existing language to the store + AddToLocaleFromJSON(langName string, source []byte) error } // ResetDefaultLocales resets the current default locales diff --git a/modules/translation/i18n/i18n_test.go b/modules/translation/i18n/i18n_test.go index 244f6ffbb3..41f85931aa 100644 --- a/modules/translation/i18n/i18n_test.go +++ b/modules/translation/i18n/i18n_test.go @@ -12,6 +12,26 @@ import ( "github.com/stretchr/testify/require" ) +var MockPluralRule PluralFormRule = func(n int64) PluralFormIndex { + if n == 0 { + return PluralFormZero + } + if n == 1 { + return PluralFormOne + } + if n >= 2 && n <= 4 { + return PluralFormFew + } + return PluralFormOther +} + +var MockPluralRuleEnglish PluralFormRule = func(n int64) PluralFormIndex { + if n == 1 { + return PluralFormOne + } + return PluralFormOther +} + func TestLocaleStore(t *testing.T) { testData1 := []byte(` .dot.name = Dot Name @@ -27,11 +47,48 @@ fmt = %[2]s %[1]s [section] sub = Changed Sub String +commits = fallback value for commits +`) + + testDataJSON2 := []byte(` +{ + "section.json": "the JSON is %s", + "section.commits": { + "one": "one %d commit", + "few": "some %d commits", + "other": "lots of %d commits" + }, + "section.incomplete": { + "few": "some %d objects (translated)" + }, + "nested": { + "outer": { + "inner": { + "json": "Hello World", + "issue": { + "one": "one %d issue", + "few": "some %d issues", + "other": "lots of %d issues" + } + } + } + } +} +`) + testDataJSON1 := []byte(` +{ + "section.incomplete": { + "one": "[untranslated] some %d object", + "other": "[untranslated] some %d objects" + } +} `) ls := NewLocaleStore() - require.NoError(t, ls.AddLocaleByIni("lang1", "Lang1", testData1, nil)) - require.NoError(t, ls.AddLocaleByIni("lang2", "Lang2", testData2, nil)) + require.NoError(t, ls.AddLocaleByIni("lang1", "Lang1", MockPluralRuleEnglish, testData1, nil)) + require.NoError(t, ls.AddLocaleByIni("lang2", "Lang2", MockPluralRule, testData2, nil)) + require.NoError(t, ls.AddToLocaleFromJSON("lang1", testDataJSON1)) + require.NoError(t, ls.AddToLocaleFromJSON("lang2", testDataJSON2)) ls.SetDefaultLang("lang1") lang1, _ := ls.Locale("lang1") @@ -56,6 +113,45 @@ sub = Changed Sub String result2 := lang2.TrHTML("section.mixed", "a&b") assert.EqualValues(t, `test value; a&b`, result2) + result = lang2.TrString("section.json", "valid") + assert.Equal(t, "the JSON is valid", result) + + result = lang2.TrString("nested.outer.inner.json") + assert.Equal(t, "Hello World", result) + + result = lang2.TrString("section.commits") + assert.Equal(t, "lots of %d commits", result) + + result2 = lang2.TrPluralString(1, "section.commits", 1) + assert.EqualValues(t, "one 1 commit", result2) + + result2 = lang2.TrPluralString(3, "section.commits", 3) + assert.EqualValues(t, "some 3 commits", result2) + + result2 = lang2.TrPluralString(8, "section.commits", 8) + assert.EqualValues(t, "lots of 8 commits", result2) + + result2 = lang2.TrPluralString(0, "section.commits") + assert.EqualValues(t, "section.commits", result2) + + result2 = lang2.TrPluralString(1, "nested.outer.inner.issue", 1) + assert.EqualValues(t, "one 1 issue", result2) + + result2 = lang2.TrPluralString(3, "nested.outer.inner.issue", 3) + assert.EqualValues(t, "some 3 issues", result2) + + result2 = lang2.TrPluralString(9, "nested.outer.inner.issue", 9) + assert.EqualValues(t, "lots of 9 issues", result2) + + result2 = lang2.TrPluralString(3, "section.incomplete", 3) + assert.EqualValues(t, "some 3 objects (translated)", result2) + + result2 = lang2.TrPluralString(1, "section.incomplete", 1) + assert.EqualValues(t, "[untranslated] some 1 object", result2) + + result2 = lang2.TrPluralString(7, "section.incomplete", 7) + assert.EqualValues(t, "[untranslated] some 7 objects", result2) + langs, descs := ls.ListLangNameDesc() assert.ElementsMatch(t, []string{"lang1", "lang2"}, langs) assert.ElementsMatch(t, []string{"Lang1", "Lang2"}, descs) @@ -77,7 +173,7 @@ c=22 `) ls := NewLocaleStore() - require.NoError(t, ls.AddLocaleByIni("lang1", "Lang1", testData1, testData2)) + require.NoError(t, ls.AddLocaleByIni("lang1", "Lang1", MockPluralRule, testData1, testData2)) lang1, _ := ls.Locale("lang1") assert.Equal(t, "11", lang1.TrString("a")) assert.Equal(t, "21", lang1.TrString("b")) @@ -118,7 +214,7 @@ func (e *errorPointerReceiver) Error() string { func TestLocaleWithTemplate(t *testing.T) { ls := NewLocaleStore() - require.NoError(t, ls.AddLocaleByIni("lang1", "Lang1", []byte(`key=%s`), nil)) + require.NoError(t, ls.AddLocaleByIni("lang1", "Lang1", MockPluralRule, []byte(`key=%s`), nil)) lang1, _ := ls.Locale("lang1") tmpl := template.New("test").Funcs(template.FuncMap{"tr": lang1.TrHTML}) @@ -181,7 +277,7 @@ func TestLocaleStoreQuirks(t *testing.T) { for _, testData := range testDataList { ls := NewLocaleStore() - err := ls.AddLocaleByIni("lang1", "Lang1", []byte("a="+testData.in), nil) + err := ls.AddLocaleByIni("lang1", "Lang1", nil, []byte("a="+testData.in), nil) lang1, _ := ls.Locale("lang1") require.NoError(t, err, testData.hint) assert.Equal(t, testData.out, lang1.TrString("a"), testData.hint) diff --git a/modules/translation/i18n/localestore.go b/modules/translation/i18n/localestore.go index 0e6ddab401..e80b2592ae 100644 --- a/modules/translation/i18n/localestore.go +++ b/modules/translation/i18n/localestore.go @@ -8,8 +8,10 @@ import ( "html/template" "slices" + "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/util" ) // This file implements the static LocaleStore that will not watch for changes @@ -18,6 +20,9 @@ type locale struct { store *localeStore langName string idxToMsgMap map[int]string // the map idx is generated by store's trKeyToIdxMap + + newStyleMessages map[string]string + pluralRule PluralFormRule } var _ Locale = (*locale)(nil) @@ -38,8 +43,19 @@ func NewLocaleStore() LocaleStore { return &localeStore{localeMap: make(map[string]*locale), trKeyToIdxMap: make(map[string]int)} } +const ( + PluralFormSeparator string = "\036" +) + +// A note about pluralization rules. +// go-i18n supports plural rules in theory. +// In practice, it relies on another library that hardcodes a list of common languages +// and their plural rules, and does not support languages not hardcoded there. +// So we pretend that all languages are English and use our own function to extract +// the correct plural form for a given count and language. + // AddLocaleByIni adds locale by ini into the store -func (store *localeStore) AddLocaleByIni(langName, langDesc string, source, moreSource []byte) error { +func (store *localeStore) AddLocaleByIni(langName, langDesc string, pluralRule PluralFormRule, source, moreSource []byte) error { if _, ok := store.localeMap[langName]; ok { return ErrLocaleAlreadyExist } @@ -47,7 +63,7 @@ func (store *localeStore) AddLocaleByIni(langName, langDesc string, source, more store.langNames = append(store.langNames, langName) store.langDescs = append(store.langDescs, langDesc) - l := &locale{store: store, langName: langName, idxToMsgMap: make(map[int]string)} + l := &locale{store: store, langName: langName, idxToMsgMap: make(map[int]string), pluralRule: pluralRule, newStyleMessages: make(map[string]string)} store.localeMap[l.langName] = l iniFile, err := setting.NewConfigProviderForLocale(source, moreSource) @@ -78,6 +94,98 @@ func (store *localeStore) AddLocaleByIni(langName, langDesc string, source, more return nil } +func RecursivelyAddTranslationsFromJSON(locale *locale, object map[string]any, prefix string) error { + for key, value := range object { + var fullkey string + if prefix != "" { + fullkey = prefix + "." + key + } else { + fullkey = key + } + + switch v := value.(type) { + case string: + // Check whether we are adding a plural form to the parent object, or a new nested JSON object. + + if key == "zero" || key == "one" || key == "two" || key == "few" || key == "many" { + locale.newStyleMessages[prefix+PluralFormSeparator+key] = v + } else if key == "other" { + locale.newStyleMessages[prefix] = v + } else { + locale.newStyleMessages[fullkey] = v + } + + case map[string]any: + err := RecursivelyAddTranslationsFromJSON(locale, v, fullkey) + if err != nil { + return err + } + + case nil: + default: + return fmt.Errorf("Unrecognized JSON value '%s'", value) + } + } + + return nil +} + +func (store *localeStore) AddToLocaleFromJSON(langName string, source []byte) error { + locale, ok := store.localeMap[langName] + if !ok { + return ErrLocaleDoesNotExist + } + + var result map[string]any + if err := json.Unmarshal(source, &result); err != nil { + return err + } + + return RecursivelyAddTranslationsFromJSON(locale, result, "") +} + +func (l *locale) LookupNewStyleMessage(trKey string) string { + if msg, ok := l.newStyleMessages[trKey]; ok { + return msg + } + return "" +} + +func (l *locale) LookupPlural(trKey string, count any) string { + n, err := util.ToInt64(count) + if err != nil { + log.Error("Invalid plural count '%s'", count) + return "" + } + + pluralForm := l.pluralRule(n) + suffix := "" + switch pluralForm { + case PluralFormZero: + suffix = PluralFormSeparator + "zero" + case PluralFormOne: + suffix = PluralFormSeparator + "one" + case PluralFormTwo: + suffix = PluralFormSeparator + "two" + case PluralFormFew: + suffix = PluralFormSeparator + "few" + case PluralFormMany: + suffix = PluralFormSeparator + "many" + case PluralFormOther: + // No suffix for the "other" string. + default: + log.Error("Invalid plural form index %d for count %d", pluralForm, count) + return "" + } + + if result, ok := l.newStyleMessages[trKey+suffix]; ok { + return result + } + + log.Error("Missing translation for plural form index %d for count %d", pluralForm, count) + return "" +} + func (store *localeStore) HasLang(langName string) bool { _, ok := store.localeMap[langName] return ok @@ -113,22 +221,37 @@ func (store *localeStore) Close() error { func (l *locale) TrString(trKey string, trArgs ...any) string { format := trKey - idx, ok := l.store.trKeyToIdxMap[trKey] - found := false - if ok { - if msg, ok := l.idxToMsgMap[idx]; ok { - format = msg // use the found translation - found = true - } else if def, ok := l.store.localeMap[l.store.defaultLang]; ok { - // try to use default locale's translation - if msg, ok := def.idxToMsgMap[idx]; ok { - format = msg + if msg := l.LookupNewStyleMessage(trKey); msg != "" { + format = msg + } else { + // First fallback: old-style translation + idx, ok := l.store.trKeyToIdxMap[trKey] + found := false + if ok { + if msg, ok := l.idxToMsgMap[idx]; ok { + format = msg // use the found translation found = true } } - } - if !found { - log.Error("Missing translation %q", trKey) + + if !found { + // Second fallback: new-style default language + if defaultLang, ok := l.store.localeMap[l.store.defaultLang]; ok { + if msg := defaultLang.LookupNewStyleMessage(trKey); msg != "" { + format = msg + } else { + // Third fallback: old-style default language + if msg, ok := defaultLang.idxToMsgMap[idx]; ok { + format = msg + found = true + } + } + } + + if !found { + log.Error("Missing translation %q", trKey) + } + } } msg, err := Format(format, trArgs...) @@ -138,7 +261,7 @@ func (l *locale) TrString(trKey string, trArgs ...any) string { return msg } -func (l *locale) TrHTML(trKey string, trArgs ...any) template.HTML { +func PrepareArgsForHTML(trArgs ...any) []any { args := slices.Clone(trArgs) for i, v := range args { switch v := v.(type) { @@ -152,7 +275,30 @@ func (l *locale) TrHTML(trKey string, trArgs ...any) template.HTML { args[i] = template.HTMLEscapeString(fmt.Sprint(v)) } } - return template.HTML(l.TrString(trKey, args...)) + return args +} + +func (l *locale) TrHTML(trKey string, trArgs ...any) template.HTML { + return template.HTML(l.TrString(trKey, PrepareArgsForHTML(trArgs...)...)) +} + +func (l *locale) TrPluralString(count any, trKey string, trArgs ...any) template.HTML { + message := l.LookupPlural(trKey, count) + + if message == "" { + if defaultLang, ok := l.store.localeMap[l.store.defaultLang]; ok { + message = defaultLang.LookupPlural(trKey, count) + } + if message == "" { + message = trKey + } + } + + message, err := Format(message, PrepareArgsForHTML(trArgs...)...) + if err != nil { + log.Error("Error whilst formatting %q in %s: %v", trKey, l.langName, err) + } + return template.HTML(message) } // HasKey returns whether a key is present in this locale or not diff --git a/modules/translation/mock.go b/modules/translation/mock.go index fe3a1502ea..4d9acce26f 100644 --- a/modules/translation/mock.go +++ b/modules/translation/mock.go @@ -31,6 +31,10 @@ func (l MockLocale) TrN(cnt any, key1, keyN string, args ...any) template.HTML { return template.HTML(key1) } +func (l MockLocale) TrPluralString(count any, trKey string, trArgs ...any) template.HTML { + return template.HTML(trKey) +} + func (l MockLocale) TrSize(s int64) ReadableSize { return ReadableSize{fmt.Sprint(s), ""} } diff --git a/modules/translation/plural_rules.go b/modules/translation/plural_rules.go new file mode 100644 index 0000000000..b8c00ceef7 --- /dev/null +++ b/modules/translation/plural_rules.go @@ -0,0 +1,253 @@ +// Copyright 2024 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +// Some useful links: +// https://www.unicode.org/cldr/charts/46/supplemental/language_plural_rules.html +// https://translate.codeberg.org/languages/$LANGUAGE_CODE/#information +// https://github.com/WeblateOrg/language-data/blob/main/languages.csv +// Note that in some cases there is ambiguity about the correct form for a given language. In this case, ask the locale's translators. + +package translation + +import ( + "strings" + + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/translation/i18n" +) + +// The constants refer to indices below in `PluralRules` and also in i18n.js, keep them in sync! +const ( + PluralRuleDefault = 0 + PluralRuleBengali = 1 + PluralRuleIcelandic = 2 + PluralRuleFilipino = 3 + PluralRuleOneForm = 4 + PluralRuleCzech = 5 + PluralRuleRussian = 6 + PluralRulePolish = 7 + PluralRuleLatvian = 8 + PluralRuleLithuanian = 9 + PluralRuleFrench = 10 + PluralRuleCatalan = 11 + PluralRuleSlovenian = 12 + PluralRuleArabic = 13 +) + +func GetPluralRuleImpl(langName string) int { + // First, check for languages with country-specific plural rules. + switch langName { + case "pt-BR": + return PluralRuleFrench + + case "pt-PT": + return PluralRuleCatalan + + default: + break + } + + // Remove the country portion of the locale name. + langName = strings.Split(strings.Split(langName, "_")[0], "-")[0] + + // When adding a new language not in the list, add its plural rule definition here. + switch langName { + case "en", "aa", "ab", "abr", "ada", "ae", "aeb", "af", "afh", "aii", "ain", "akk", "ale", "aln", "alt", "ami", "an", "ang", "anp", "apc", "arc", "arp", "arq", "arw", "arz", "asa", "ast", "av", "avk", "awa", "ayc", "az", "azb", "ba", "bal", "ban", "bar", "bas", "bbc", "bci", "bej", "bem", "ber", "bew", "bez", "bg", "bgc", "bgn", "bhb", "bhi", "bi", "bik", "bin", "bjj", "bjn", "bla", "bnt", "bqi", "bra", "brb", "brh", "brx", "bua", "bug", "bum", "byn", "cad", "cak", "car", "ce", "cgg", "ch", "chb", "chg", "chk", "chm", "chn", "cho", "chp", "chr", "chy", "ckb", "co", "cop", "cpe", "cpf", "cr", "crp", "cu", "cv", "da", "dak", "dar", "dcc", "de", "del", "den", "dgr", "din", "dje", "dnj", "dnk", "dru", "dry", "dua", "dum", "dv", "dyu", "ee", "efi", "egl", "egy", "eka", "el", "elx", "enm", "eo", "et", "eu", "ewo", "ext", "fan", "fat", "fbl", "ffm", "fi", "fj", "fo", "fon", "frk", "frm", "fro", "frr", "frs", "fuq", "fur", "fuv", "fvr", "fy", "gaa", "gay", "gba", "gbm", "gez", "gil", "gl", "glk", "gmh", "gn", "goh", "gom", "gon", "gor", "got", "grb", "gsw", "guc", "gum", "gur", "guz", "gwi", "ha", "hai", "haw", "haz", "hil", "hit", "hmn", "hnd", "hne", "hno", "ho", "hoc", "hoj", "hrx", "ht", "hu", "hup", "hus", "hz", "ia", "iba", "ibb", "ie", "ik", "ilo", "inh", "io", "jam", "jgo", "jmc", "jpr", "jrb", "ka", "kaa", "kac", "kaj", "kam", "kaw", "kbd", "kcg", "kfr", "kfy", "kg", "kha", "khn", "kho", "ki", "kj", "kk", "kkj", "kl", "kln", "kmb", "kmr", "kok", "kpe", "kr", "krc", "kri", "krl", "kru", "ks", "ksb", "ku", "kum", "kut", "kv", "kxm", "ky", "la", "lad", "laj", "lam", "lb", "lez", "lfn", "lg", "li", "lij", "ljp", "lki", "lmn", "lmo", "lol", "loz", "lrc", "lu", "lua", "lui", "lun", "luo", "lus", "luy", "luz", "mad", "mag", "mai", "mak", "man", "mas", "mdf", "mdh", "mdr", "men", "mer", "mfa", "mga", "mgh", "mgo", "mh", "mhr", "mic", "min", "mjw", "ml", "mn", "mnc", "mni", "mnw", "moe", "moh", "mos", "mr", "mrh", "mtr", "mus", "mwk", "mwl", "mwr", "mxc", "myv", "myx", "mzn", "na", "nah", "nap", "nb", "nd", "ndc", "nds", "ne", "new", "ng", "ngl", "nia", "nij", "niu", "nl", "nn", "nnh", "nod", "noe", "nog", "non", "nr", "nuk", "nv", "nwc", "ny", "nym", "nyn", "nyo", "nzi", "oj", "om", "or", "os", "ota", "otk", "ovd", "pag", "pal", "pam", "pap", "pau", "pbb", "pdt", "peo", "phn", "pi", "pms", "pon", "pro", "ps", "pwn", "qu", "quc", "qug", "qya", "raj", "rap", "rar", "rcf", "rej", "rhg", "rif", "rkt", "rm", "rmt", "rn", "rng", "rof", "rom", "rue", "rup", "rw", "rwk", "sad", "sai", "sam", "saq", "sas", "sc", "sck", "sco", "sd", "sdh", "sef", "seh", "sel", "sga", "sgn", "sgs", "shn", "sid", "sjd", "skr", "sm", "sml", "sn", "snk", "so", "sog", "sou", "sq", "srn", "srr", "ss", "ssy", "st", "suk", "sus", "sux", "sv", "sw", "swg", "swv", "sxu", "syc", "syl", "syr", "szy", "ta", "tay", "tcy", "te", "tem", "teo", "ter", "tet", "tig", "tiv", "tk", "tkl", "tli", "tly", "tmh", "tn", "tog", "tr", "trv", "ts", "tsg", "tsi", "tsj", "tts", "tum", "tvl", "tw", "ty", "tyv", "tzj", "tzl", "udm", "ug", "uga", "umb", "und", "unr", "ur", "uz", "vai", "ve", "vls", "vmf", "vmw", "vo", "vot", "vro", "vun", "wae", "wal", "war", "was", "wbq", "wbr", "wep", "wtm", "xal", "xh", "xnr", "xog", "yao", "yap", "yi", "yua", "za", "zap", "zbl", "zen", "zgh", "zun", "zza": + return PluralRuleDefault + + case "ach", "ady", "ak", "am", "arn", "as", "bh", "bho", "bn", "csw", "doi", "fa", "ff", "frc", "frp", "gu", "gug", "gun", "guw", "hi", "hy", "kab", "kn", "ln", "mfe", "mg", "mi", "mia", "nso", "oc", "pa", "pcm", "pt", "qdt", "qtp", "si", "tg", "ti", "wa", "zu": + return PluralRuleBengali + + case "is": + return PluralRuleIcelandic + + case "fil": + return PluralRuleFilipino + + case "ace", "ay", "bm", "bo", "cdo", "cpx", "crh", "dz", "gan", "hak", "hnj", "hsn", "id", "ig", "ii", "ja", "jbo", "jv", "kde", "kea", "km", "ko", "kos", "lkt", "lo", "lzh", "ms", "my", "nan", "nqo", "osa", "sah", "ses", "sg", "son", "su", "th", "tlh", "to", "tok", "tpi", "tt", "vi", "wo", "wuu", "yo", "yue", "zh": + return PluralRuleOneForm + + case "cpp", "cs", "sk": + return PluralRuleCzech + + case "be", "bs", "cnr", "hr", "ru", "sr", "uk", "wen": + return PluralRuleRussian + + case "csb", "pl", "szl": + return PluralRulePolish + + case "lv", "prg": + return PluralRuleLatvian + + case "lt": + return PluralRuleLithuanian + + case "fr": + return PluralRuleFrench + + case "ca", "es", "it": + return PluralRuleCatalan + + case "sl": + return PluralRuleSlovenian + + case "ar": + return PluralRuleArabic + + default: + break + } + + log.Error("No plural rule defined for language %s", langName) + return PluralRuleDefault +} + +var PluralRules = []i18n.PluralFormRule{ + // [ 0] Common 2-form, e.g. English, German + func(n int64) i18n.PluralFormIndex { + if n != 1 { + return i18n.PluralFormOther + } + return i18n.PluralFormOne + }, + + // [ 1] Bengali + func(n int64) i18n.PluralFormIndex { + if n > 1 { + return i18n.PluralFormOther + } + return i18n.PluralFormOne + }, + + // [ 2] Icelandic + func(n int64) i18n.PluralFormIndex { + if n%10 != 1 || n%100 == 11 { + return i18n.PluralFormOther + } + return i18n.PluralFormOne + }, + + // [ 3] Filipino + func(n int64) i18n.PluralFormIndex { + if n != 1 && n != 2 && n != 3 && (n%10 == 4 || n%10 == 6 || n%10 == 9) { + return i18n.PluralFormOther + } + return i18n.PluralFormOne + }, + + // [ 4] OneForm + func(n int64) i18n.PluralFormIndex { + return i18n.PluralFormOther + }, + + // [ 5] Czech + func(n int64) i18n.PluralFormIndex { + if n == 1 { + return i18n.PluralFormOne + } + if n >= 2 && n <= 4 { + return i18n.PluralFormFew + } + return i18n.PluralFormOther + }, + + // [ 6] Russian + func(n int64) i18n.PluralFormIndex { + if n%10 == 1 && n%100 != 11 { + return i18n.PluralFormOne + } + if n%10 >= 2 && n%10 <= 4 && (n%100 < 10 || n%100 >= 20) { + return i18n.PluralFormFew + } + return i18n.PluralFormMany + }, + + // [ 7] Polish + func(n int64) i18n.PluralFormIndex { + if n == 1 { + return i18n.PluralFormOne + } + if n%10 >= 2 && n%10 <= 4 && (n%100 < 10 || n%100 >= 20) { + return i18n.PluralFormFew + } + return i18n.PluralFormMany + }, + + // [ 8] Latvian + func(n int64) i18n.PluralFormIndex { + if n%10 == 0 || n%100 >= 11 && n%100 <= 19 { + return i18n.PluralFormZero + } + if n%10 == 1 && n%100 != 11 { + return i18n.PluralFormOne + } + return i18n.PluralFormOther + }, + + // [ 9] Lithuanian + func(n int64) i18n.PluralFormIndex { + if n%10 == 1 && (n%100 < 11 || n%100 > 19) { + return i18n.PluralFormOne + } + if n%10 >= 2 && n%10 <= 9 && (n%100 < 11 || n%100 > 19) { + return i18n.PluralFormFew + } + return i18n.PluralFormMany + }, + + // [10] French + func(n int64) i18n.PluralFormIndex { + if n == 0 || n == 1 { + return i18n.PluralFormOne + } + if n != 0 && n%1000000 == 0 { + return i18n.PluralFormMany + } + return i18n.PluralFormOther + }, + + // [11] Catalan + func(n int64) i18n.PluralFormIndex { + if n == 1 { + return i18n.PluralFormOne + } + if n != 0 && n%1000000 == 0 { + return i18n.PluralFormMany + } + return i18n.PluralFormOther + }, + + // [12] Slovenian + func(n int64) i18n.PluralFormIndex { + if n%100 == 1 { + return i18n.PluralFormOne + } + if n%100 == 2 { + return i18n.PluralFormTwo + } + if n%100 == 3 || n%100 == 4 { + return i18n.PluralFormFew + } + return i18n.PluralFormOther + }, + + // [13] Arabic + func(n int64) i18n.PluralFormIndex { + if n == 0 { + return i18n.PluralFormZero + } + if n == 1 { + return i18n.PluralFormOne + } + if n == 2 { + return i18n.PluralFormTwo + } + if n%100 >= 3 && n%100 <= 10 { + return i18n.PluralFormFew + } + if n%100 >= 11 { + return i18n.PluralFormMany + } + return i18n.PluralFormOther + }, +} diff --git a/modules/translation/translation.go b/modules/translation/translation.go index 6687d3d817..7d1c627c84 100644 --- a/modules/translation/translation.go +++ b/modules/translation/translation.go @@ -32,6 +32,9 @@ type Locale interface { TrString(string, ...any) string Tr(key string, args ...any) template.HTML + // New-style pluralized strings + TrPluralString(count any, trKey string, trArgs ...any) template.HTML + // Old-style pseudo-pluralized strings, deprecated TrN(cnt any, key1, keyN string, args ...any) template.HTML TrSize(size int64) ReadableSize @@ -100,8 +103,17 @@ func InitLocales(ctx context.Context) { } key := "locale_" + setting.Langs[i] + ".ini" - if err = i18n.DefaultLocales.AddLocaleByIni(setting.Langs[i], setting.Names[i], localeDataBase, localeData[key]); err != nil { - log.Error("Failed to set messages to %s: %v", setting.Langs[i], err) + if err = i18n.DefaultLocales.AddLocaleByIni(setting.Langs[i], setting.Names[i], PluralRules[GetPluralRuleImpl(setting.Langs[i])], localeDataBase, localeData[key]); err != nil { + log.Error("Failed to set old-style messages to %s: %v", setting.Langs[i], err) + } + + key = "locale_next/locale_" + setting.Langs[i] + ".json" + if bytes, err := options.AssetFS().ReadFile(key); err == nil { + if err = i18n.DefaultLocales.AddToLocaleFromJSON(setting.Langs[i], bytes); err != nil { + log.Error("Failed to add new-style messages to %s: %v", setting.Langs[i], err) + } + } else { + log.Error("Failed to open new-style messages for %s: %v", setting.Langs[i], err) } } if len(setting.Langs) != 0 { diff --git a/modules/translation/translation_test.go b/modules/translation/translation_test.go index bffbb155ca..5b3eefb355 100644 --- a/modules/translation/translation_test.go +++ b/modules/translation/translation_test.go @@ -48,3 +48,111 @@ func TestPrettyNumber(t *testing.T) { assert.EqualValues(t, "1,000,000", l.PrettyNumber(1000000)) assert.EqualValues(t, "1,000,000.1", l.PrettyNumber(1000000.1)) } + +func TestGetPluralRule(t *testing.T) { + assert.Equal(t, PluralRuleDefault, GetPluralRuleImpl("en")) + assert.Equal(t, PluralRuleDefault, GetPluralRuleImpl("en-US")) + assert.Equal(t, PluralRuleDefault, GetPluralRuleImpl("en_UK")) + assert.Equal(t, PluralRuleDefault, GetPluralRuleImpl("nds")) + assert.Equal(t, PluralRuleDefault, GetPluralRuleImpl("de-DE")) + + assert.Equal(t, PluralRuleOneForm, GetPluralRuleImpl("zh")) + assert.Equal(t, PluralRuleOneForm, GetPluralRuleImpl("ja")) + + assert.Equal(t, PluralRuleBengali, GetPluralRuleImpl("bn")) + + assert.Equal(t, PluralRuleIcelandic, GetPluralRuleImpl("is")) + + assert.Equal(t, PluralRuleFilipino, GetPluralRuleImpl("fil")) + + assert.Equal(t, PluralRuleCzech, GetPluralRuleImpl("cs")) + + assert.Equal(t, PluralRuleRussian, GetPluralRuleImpl("ru")) + + assert.Equal(t, PluralRulePolish, GetPluralRuleImpl("pl")) + + assert.Equal(t, PluralRuleLatvian, GetPluralRuleImpl("lv")) + + assert.Equal(t, PluralRuleLithuanian, GetPluralRuleImpl("lt")) + + assert.Equal(t, PluralRuleFrench, GetPluralRuleImpl("fr")) + + assert.Equal(t, PluralRuleCatalan, GetPluralRuleImpl("ca")) + + assert.Equal(t, PluralRuleSlovenian, GetPluralRuleImpl("sl")) + + assert.Equal(t, PluralRuleArabic, GetPluralRuleImpl("ar")) + + assert.Equal(t, PluralRuleCatalan, GetPluralRuleImpl("pt-PT")) + assert.Equal(t, PluralRuleFrench, GetPluralRuleImpl("pt-BR")) + + assert.Equal(t, PluralRuleDefault, GetPluralRuleImpl("invalid")) +} + +func TestApplyPluralRule(t *testing.T) { + testCases := []struct { + expect i18n.PluralFormIndex + pluralRule int + values []int64 + }{ + {i18n.PluralFormOne, PluralRuleDefault, []int64{1}}, + {i18n.PluralFormOther, PluralRuleDefault, []int64{0, 2, 10, 256}}, + + {i18n.PluralFormOther, PluralRuleOneForm, []int64{0, 1, 2}}, + + {i18n.PluralFormOne, PluralRuleBengali, []int64{0, 1}}, + {i18n.PluralFormOther, PluralRuleBengali, []int64{2, 10, 256}}, + + {i18n.PluralFormOne, PluralRuleIcelandic, []int64{1, 21, 31}}, + {i18n.PluralFormOther, PluralRuleIcelandic, []int64{0, 2, 11, 15, 256}}, + + {i18n.PluralFormOne, PluralRuleFilipino, []int64{0, 1, 2, 3, 5, 7, 8, 10, 11, 12, 257}}, + {i18n.PluralFormOther, PluralRuleFilipino, []int64{4, 6, 9, 14, 16, 19, 256}}, + + {i18n.PluralFormOne, PluralRuleCzech, []int64{1}}, + {i18n.PluralFormFew, PluralRuleCzech, []int64{2, 3, 4}}, + {i18n.PluralFormOther, PluralRuleCzech, []int64{5, 0, 12, 78, 254}}, + + {i18n.PluralFormOne, PluralRuleRussian, []int64{1, 21, 31}}, + {i18n.PluralFormFew, PluralRuleRussian, []int64{2, 23, 34}}, + {i18n.PluralFormMany, PluralRuleRussian, []int64{0, 5, 11, 37, 111, 256}}, + + {i18n.PluralFormOne, PluralRulePolish, []int64{1}}, + {i18n.PluralFormFew, PluralRulePolish, []int64{2, 23, 34}}, + {i18n.PluralFormMany, PluralRulePolish, []int64{0, 5, 11, 21, 37, 256}}, + + {i18n.PluralFormZero, PluralRuleLatvian, []int64{0, 10, 11, 17}}, + {i18n.PluralFormOne, PluralRuleLatvian, []int64{1, 21, 71}}, + {i18n.PluralFormOther, PluralRuleLatvian, []int64{2, 7, 22, 23, 256}}, + + {i18n.PluralFormOne, PluralRuleLithuanian, []int64{1, 21, 31}}, + {i18n.PluralFormFew, PluralRuleLithuanian, []int64{2, 5, 9, 23, 34, 256}}, + {i18n.PluralFormMany, PluralRuleLithuanian, []int64{0, 10, 11, 18}}, + + {i18n.PluralFormOne, PluralRuleFrench, []int64{0, 1}}, + {i18n.PluralFormMany, PluralRuleFrench, []int64{1000000, 2000000}}, + {i18n.PluralFormOther, PluralRuleFrench, []int64{2, 4, 10, 256}}, + + {i18n.PluralFormOne, PluralRuleCatalan, []int64{1}}, + {i18n.PluralFormMany, PluralRuleCatalan, []int64{1000000, 2000000}}, + {i18n.PluralFormOther, PluralRuleCatalan, []int64{0, 2, 4, 10, 256}}, + + {i18n.PluralFormOne, PluralRuleSlovenian, []int64{1, 101, 201, 501}}, + {i18n.PluralFormTwo, PluralRuleSlovenian, []int64{2, 102, 202, 502}}, + {i18n.PluralFormFew, PluralRuleSlovenian, []int64{3, 103, 203, 503, 4, 104, 204, 504}}, + {i18n.PluralFormOther, PluralRuleSlovenian, []int64{0, 5, 11, 12, 20, 256}}, + + {i18n.PluralFormZero, PluralRuleArabic, []int64{0}}, + {i18n.PluralFormOne, PluralRuleArabic, []int64{1}}, + {i18n.PluralFormTwo, PluralRuleArabic, []int64{2}}, + {i18n.PluralFormFew, PluralRuleArabic, []int64{3, 4, 9, 10, 103, 104}}, + {i18n.PluralFormMany, PluralRuleArabic, []int64{11, 12, 13, 14, 17, 111, 256}}, + {i18n.PluralFormOther, PluralRuleArabic, []int64{100, 101, 102}}, + } + + for _, tc := range testCases { + for _, n := range tc.values { + assert.Equal(t, tc.expect, PluralRules[tc.pluralRule](n), "Testcase for plural rule %d, value %d", tc.pluralRule, n) + } + } +} diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index eaeab11a9d..127e629e80 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -190,7 +190,6 @@ commit_kind = Search commits... runner_kind = Search runners... no_results = No matching results found. issue_kind = Search issues... -milestone_kind = Search milestones... pull_kind = Search pulls... keyword_search_unavailable = Searching by keyword is currently not available. Please contact the site administrator. @@ -1887,10 +1886,6 @@ pulls.nothing_to_compare_have_tag = The selected branch/tag are equal. pulls.nothing_to_compare_and_allow_empty_pr = These branches are equal. This PR will be empty. pulls.has_pull_request = `A pull request between these branches already exists: %[2]s#%[3]d` pulls.create = Create pull request -pulls.title_desc_one = wants to merge %[1]d commit from %[2]s into %[3]s -pulls.title_desc_few = wants to merge %[1]d commits from %[2]s into %[3]s -pulls.merged_title_desc_one = merged %[1]d commit from %[2]s into %[3]s %[4]s -pulls.merged_title_desc_few = merged %[1]d commits from %[2]s into %[3]s %[4]s pulls.change_target_branch_at = `changed target branch from %s to %s %s` pulls.tab_conversation = Conversation pulls.tab_commits = Commits diff --git a/options/locale_next/locale_ar.json b/options/locale_next/locale_ar.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/options/locale_next/locale_ar.json @@ -0,0 +1 @@ +{} diff --git a/options/locale_next/locale_be.json b/options/locale_next/locale_be.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/options/locale_next/locale_be.json @@ -0,0 +1 @@ +{} diff --git a/options/locale_next/locale_bg.json b/options/locale_next/locale_bg.json new file mode 100644 index 0000000000..bec72c556a --- /dev/null +++ b/options/locale_next/locale_bg.json @@ -0,0 +1,14 @@ +{ + "repo": { + "pulls": { + "merged_title_desc": { + "one": "сля %[1]d подаване от %[2]s в %[3]s %[4]s", + "other": "сля %[1]d подавания от %[2]s в %[3]s %[4]s" + }, + "title_desc": { + "one": "иска да слее %[1]d подаване от %[2]s в %[3]s", + "other": "иска да слее %[1]d подавания от %[2]s в %[3]s" + } + } + } +} diff --git a/options/locale_next/locale_bn.json b/options/locale_next/locale_bn.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/options/locale_next/locale_bn.json @@ -0,0 +1 @@ +{} diff --git a/options/locale_next/locale_bs.json b/options/locale_next/locale_bs.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/options/locale_next/locale_bs.json @@ -0,0 +1 @@ +{} diff --git a/options/locale_next/locale_ca.json b/options/locale_next/locale_ca.json new file mode 100644 index 0000000000..8aee80092d --- /dev/null +++ b/options/locale_next/locale_ca.json @@ -0,0 +1,5 @@ +{ + "search": { + "milestone_kind": "Cerca fites..." + } +} diff --git a/options/locale_next/locale_cs-CZ.json b/options/locale_next/locale_cs-CZ.json new file mode 100644 index 0000000000..373b9dc31e --- /dev/null +++ b/options/locale_next/locale_cs-CZ.json @@ -0,0 +1,17 @@ +{ + "repo": { + "pulls": { + "merged_title_desc": { + "one": "sloučil %[1]d commit z %[2]s do %[3]s %[4]s", + "other": "sloučil %[1]d commity z větve %[2]s do větve %[3]s před %[4]s" + }, + "title_desc": { + "one": "žádá o sloučení %[1]d commitu z %[2]s do %[3]s", + "other": "chce sloučit %[1]d commity z větve %[2]s do %[3]s" + } + } + }, + "search": { + "milestone_kind": "Hledat milníky..." + } +} diff --git a/options/locale_next/locale_da.json b/options/locale_next/locale_da.json new file mode 100644 index 0000000000..834f66024f --- /dev/null +++ b/options/locale_next/locale_da.json @@ -0,0 +1,5 @@ +{ + "search": { + "milestone_kind": "Søg milepæle..." + } +} diff --git a/options/locale_next/locale_de-DE.json b/options/locale_next/locale_de-DE.json new file mode 100644 index 0000000000..82e4ea54d4 --- /dev/null +++ b/options/locale_next/locale_de-DE.json @@ -0,0 +1,17 @@ +{ + "repo": { + "pulls": { + "merged_title_desc": { + "one": "hat %[1]d Commit von %[2]s nach %[3]s %[4]s zusammengeführt", + "other": "hat %[1]d Commits von %[2]s nach %[3]s %[4]s zusammengeführt" + }, + "title_desc": { + "one": "möchte %[1]d Commit von %[2]s nach %[3]s zusammenführen", + "other": "möchte %[1]d Commits von %[2]s nach %[3]s zusammenführen" + } + } + }, + "search": { + "milestone_kind": "Meilensteine suchen …" + } +} diff --git a/options/locale_next/locale_el-GR.json b/options/locale_next/locale_el-GR.json new file mode 100644 index 0000000000..9fa112cf0f --- /dev/null +++ b/options/locale_next/locale_el-GR.json @@ -0,0 +1,17 @@ +{ + "repo": { + "pulls": { + "merged_title_desc": { + "one": "συγχώνευσε %[1]d υποβολή από τον κλάδο %[2]s στον κλάδο %[3]s %[4]s", + "other": "συγχώνευσε %[1]d υποβολές από %[2]s σε %[3]s %[4]s" + }, + "title_desc": { + "one": ": θα ήθελε να συγχωνεύσει %[1]d υποβολή από τον κλάδο %[2]s στον κλάδο %[3]s", + "other": "θέλει να συγχωνεύσει %[1]d υποβολές από %[2]s σε %[3]s" + } + } + }, + "search": { + "milestone_kind": "Αναζήτηση ορόσημων..." + } +} diff --git a/options/locale_next/locale_en-US.json b/options/locale_next/locale_en-US.json new file mode 100644 index 0000000000..64e3e50abc --- /dev/null +++ b/options/locale_next/locale_en-US.json @@ -0,0 +1,17 @@ +{ + "repo": { + "pulls": { + "merged_title_desc": { + "one": "merged %[1]d commit from %[2]s into %[3]s %[4]s", + "other": "merged %[1]d commits from %[2]s into %[3]s %[4]s" + }, + "title_desc": { + "one": "wants to merge %[1]d commit from %[2]s into %[3]s", + "other": "wants to merge %[1]d commits from %[2]s into %[3]s" + } + } + }, + "search": { + "milestone_kind": "Search milestones..." + } +} diff --git a/options/locale_next/locale_eo.json b/options/locale_next/locale_eo.json new file mode 100644 index 0000000000..c57b462d5f --- /dev/null +++ b/options/locale_next/locale_eo.json @@ -0,0 +1,5 @@ +{ + "search": { + "milestone_kind": "Serĉi celojn..." + } +} diff --git a/options/locale_next/locale_es-ES.json b/options/locale_next/locale_es-ES.json new file mode 100644 index 0000000000..7313cdc0a7 --- /dev/null +++ b/options/locale_next/locale_es-ES.json @@ -0,0 +1,17 @@ +{ + "repo": { + "pulls": { + "merged_title_desc": { + "one": "fusionó %[1]d commit de %[2]s en %[3]s %[4]s", + "other": "fusionó %[1]d commits de %[2]s en %[3]s %[4]s" + }, + "title_desc": { + "one": "quiere fusionar %[1]d commit de %[2]s en %[3]s", + "other": "quiere fusionar %[1]d commits de %[2]s en %[3]s" + } + } + }, + "search": { + "milestone_kind": "Buscar hitos…" + } +} diff --git a/options/locale_next/locale_et.json b/options/locale_next/locale_et.json new file mode 100644 index 0000000000..ac009856db --- /dev/null +++ b/options/locale_next/locale_et.json @@ -0,0 +1,5 @@ +{ + "search": { + "milestone_kind": "Otsi verstapostid..." + } +} diff --git a/options/locale_next/locale_fa-IR.json b/options/locale_next/locale_fa-IR.json new file mode 100644 index 0000000000..6964db4934 --- /dev/null +++ b/options/locale_next/locale_fa-IR.json @@ -0,0 +1,12 @@ +{ + "repo": { + "pulls": { + "merged_title_desc": { + "other": "%[1]d کامیت ادغام شده از %[2]s به %[3]s %[4]s" + }, + "title_desc": { + "other": "قصد ادغام %[1]d تغییر را از %[2]s به %[3]s دارد" + } + } + } +} diff --git a/options/locale_next/locale_fi-FI.json b/options/locale_next/locale_fi-FI.json new file mode 100644 index 0000000000..88a2110bcb --- /dev/null +++ b/options/locale_next/locale_fi-FI.json @@ -0,0 +1,15 @@ +{ + "repo": { + "pulls": { + "merged_title_desc": { + "other": "yhdistetty %[1]d committia lähteestä %[2]s kohteeseen %[3]s %[4]s" + }, + "title_desc": { + "other": "haluaa yhdistää %[1]d committia lähteestä %[2]s kohteeseen %[3]s" + } + } + }, + "search": { + "milestone_kind": "Etsi merkkipaaluja..." + } +} diff --git a/options/locale_next/locale_fil.json b/options/locale_next/locale_fil.json new file mode 100644 index 0000000000..6be27dbb8c --- /dev/null +++ b/options/locale_next/locale_fil.json @@ -0,0 +1,17 @@ +{ + "repo": { + "pulls": { + "merged_title_desc": { + "one": "isinali ang %[1]d commit mula%[2]s patungong %[3]s %[4]s", + "other": "isinali ang %[1]d mga commit mula sa %[2]s patungong %[3]s %[4]s" + }, + "title_desc": { + "one": "hinihiling na isama ang %[1]d commit mula %[2]s patungong %[3]s", + "other": "hiniling na isama ang %[1]d mga commit mula sa %[2]s patungong %[3]s" + } + } + }, + "search": { + "milestone_kind": "Maghanap ng mga milestone…" + } +} diff --git a/options/locale_next/locale_fr-FR.json b/options/locale_next/locale_fr-FR.json new file mode 100644 index 0000000000..995a2be8bb --- /dev/null +++ b/options/locale_next/locale_fr-FR.json @@ -0,0 +1,17 @@ +{ + "repo": { + "pulls": { + "merged_title_desc": { + "one": "fusionné %[1]d commit depuis %[2]s vers %[3]s %[4]s", + "other": "a fusionné %[1]d révision(s) à partir de %[2]s vers %[3]s %[4]s" + }, + "title_desc": { + "one": "veut fusionner %[1]d commit depuis %[2]s vers %[3]s", + "other": "souhaite fusionner %[1]d révision(s) depuis %[2]s vers %[3]s" + } + } + }, + "search": { + "milestone_kind": "Recherche dans les jalons..." + } +} diff --git a/options/locale_next/locale_gl.json b/options/locale_next/locale_gl.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/options/locale_next/locale_gl.json @@ -0,0 +1 @@ +{} diff --git a/options/locale_next/locale_hi.json b/options/locale_next/locale_hi.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/options/locale_next/locale_hi.json @@ -0,0 +1 @@ +{} diff --git a/options/locale_next/locale_hu-HU.json b/options/locale_next/locale_hu-HU.json new file mode 100644 index 0000000000..2a21d68095 --- /dev/null +++ b/options/locale_next/locale_hu-HU.json @@ -0,0 +1,15 @@ +{ + "repo": { + "pulls": { + "merged_title_desc": { + "other": "egyesítve %[1]d változás(ok) a %[2]s-ból %[3]s-ba %[4]s" + }, + "title_desc": { + "other": "egyesíteni szeretné %[1]d változás(oka)t a(z) %[2]s-ból %[3]s-ba" + } + } + }, + "search": { + "milestone_kind": "Mérföldkövek keresése..." + } +} diff --git a/options/locale_next/locale_id-ID.json b/options/locale_next/locale_id-ID.json new file mode 100644 index 0000000000..13580f3cfe --- /dev/null +++ b/options/locale_next/locale_id-ID.json @@ -0,0 +1,12 @@ +{ + "repo": { + "pulls": { + "merged_title_desc": { + "other": "commit %[1]d telah digabungkan dari %[2]s menjadi %[3]s %[4]s" + }, + "title_desc": { + "other": "ingin menggabungkan komit %[1]d dari %[2]s menuju %[3]s" + } + } + } +} diff --git a/options/locale_next/locale_is-IS.json b/options/locale_next/locale_is-IS.json new file mode 100644 index 0000000000..40d5a7e7aa --- /dev/null +++ b/options/locale_next/locale_is-IS.json @@ -0,0 +1,9 @@ +{ + "repo": { + "pulls": { + "title_desc": { + "other": "vill sameina %[1]d framlög frá %[2]s í %[3]s" + } + } + } +} diff --git a/options/locale_next/locale_it-IT.json b/options/locale_next/locale_it-IT.json new file mode 100644 index 0000000000..61cb012433 --- /dev/null +++ b/options/locale_next/locale_it-IT.json @@ -0,0 +1,17 @@ +{ + "repo": { + "pulls": { + "merged_title_desc": { + "one": "ha fuso %[1]d commit da %[2]s in %[3]s %[4]s", + "other": "ha unito %[1]d commit da %[2]s a %[3]s %[4]s" + }, + "title_desc": { + "one": "vuole fondere %[1]d commit da %[2]s in %[3]s", + "other": "vuole unire %[1]d commit da %[2]s a %[3]s" + } + } + }, + "search": { + "milestone_kind": "Ricerca tappe..." + } +} diff --git a/options/locale_next/locale_ja-JP.json b/options/locale_next/locale_ja-JP.json new file mode 100644 index 0000000000..447ee8ae22 --- /dev/null +++ b/options/locale_next/locale_ja-JP.json @@ -0,0 +1,15 @@ +{ + "repo": { + "pulls": { + "merged_title_desc": { + "other": "が %[1]d 個のコミットを %[2]s から %[3]s へマージ %[4]s" + }, + "title_desc": { + "other": "が %[2]s から %[3]s への %[1]d コミットのマージを希望しています" + } + } + }, + "search": { + "milestone_kind": "マイルストーンを検索..." + } +} diff --git a/options/locale_next/locale_ko-KR.json b/options/locale_next/locale_ko-KR.json new file mode 100644 index 0000000000..1beaec4627 --- /dev/null +++ b/options/locale_next/locale_ko-KR.json @@ -0,0 +1,12 @@ +{ + "repo": { + "pulls": { + "merged_title_desc": { + "other": "님이 %[2]s 에서 %[3]s 로 %[1]d 커밋을 %[4]s 병합함" + }, + "title_desc": { + "other": "%[2]s 에서 %[3]s 로 %[1]d개의 커밋들을 병합하려함" + } + } + } +} diff --git a/options/locale_next/locale_lt.json b/options/locale_next/locale_lt.json new file mode 100644 index 0000000000..cce5782546 --- /dev/null +++ b/options/locale_next/locale_lt.json @@ -0,0 +1,5 @@ +{ + "search": { + "milestone_kind": "Ieškoti gairių..." + } +} diff --git a/options/locale_next/locale_lv-LV.json b/options/locale_next/locale_lv-LV.json new file mode 100644 index 0000000000..e338556aab --- /dev/null +++ b/options/locale_next/locale_lv-LV.json @@ -0,0 +1,17 @@ +{ + "repo": { + "pulls": { + "merged_title_desc": { + "one": "iekļāva %[1]d iesūtījumu no %[2]s %[3]s %[4]s", + "other": "Iekļāva %[1]d iesūtījumus no %[2]s zarā %[3]s %[4]s" + }, + "title_desc": { + "one": "vēlas iekļaut %[1]d iesūtījumu no %[2]s %[3]s", + "other": "vēlas iekļaut %[1]d iesūtījumus no %[2]s zarā %[3]s" + } + } + }, + "search": { + "milestone_kind": "Meklēt atskaites punktus..." + } +} diff --git a/options/locale_next/locale_ml-IN.json b/options/locale_next/locale_ml-IN.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/options/locale_next/locale_ml-IN.json @@ -0,0 +1 @@ +{} diff --git a/options/locale_next/locale_nb_NO.json b/options/locale_next/locale_nb_NO.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/options/locale_next/locale_nb_NO.json @@ -0,0 +1 @@ +{} diff --git a/options/locale_next/locale_nds.json b/options/locale_next/locale_nds.json new file mode 100644 index 0000000000..a141362816 --- /dev/null +++ b/options/locale_next/locale_nds.json @@ -0,0 +1,17 @@ +{ + "repo": { + "pulls": { + "merged_title_desc": { + "one": "hett %[1]d Kommitteren vun %[2]s na %[3]s %[4]s tosamenföhrt", + "other": "hett %[1]d Kommitterens vun %[2]s na %[3]s %[4]s tosamenföhrt" + }, + "title_desc": { + "one": "will %[1]d Kommitteren vun %[2]s na %[3]s tosamenföhren", + "other": "will %[1]d Kommitterens vun %[2]s na %[3]s tosamenföhren" + } + } + }, + "search": { + "milestone_kind": "In Markstenen söken …" + } +} diff --git a/options/locale_next/locale_nl-NL.json b/options/locale_next/locale_nl-NL.json new file mode 100644 index 0000000000..9bbfc0fecd --- /dev/null +++ b/options/locale_next/locale_nl-NL.json @@ -0,0 +1,17 @@ +{ + "repo": { + "pulls": { + "merged_title_desc": { + "one": "heeft %[1]d commit van %[2]s samengevoegd in %[3]s %[4]s", + "other": "heeft %[1]d commits samengevoegd van %[2]s naar %[3]s %[4]s" + }, + "title_desc": { + "one": "wilt %[1]d commit van %[2]s samenvoegen in %[3]s", + "other": "wilt %[1]d commits van %[2]s samenvoegen met %[3]s" + } + } + }, + "search": { + "milestone_kind": "Zoek mijlpalen..." + } +} diff --git a/options/locale_next/locale_pl-PL.json b/options/locale_next/locale_pl-PL.json new file mode 100644 index 0000000000..1f2def3ea0 --- /dev/null +++ b/options/locale_next/locale_pl-PL.json @@ -0,0 +1,15 @@ +{ + "repo": { + "pulls": { + "merged_title_desc": { + "many": "scala %[1]d commity/ów z %[2]s do %[3]s %[4]s" + }, + "title_desc": { + "many": "chce scalić %[1]d commity/ów z %[2]s do %[3]s" + } + } + }, + "search": { + "milestone_kind": "Wyszukaj kamienie milowe..." + } +} diff --git a/options/locale_next/locale_pt-BR.json b/options/locale_next/locale_pt-BR.json new file mode 100644 index 0000000000..e7758ef1bf --- /dev/null +++ b/options/locale_next/locale_pt-BR.json @@ -0,0 +1,17 @@ +{ + "repo": { + "pulls": { + "merged_title_desc": { + "one": "mesclou %[1]d commit de %[2]s em %[3]s %[4]s", + "other": "mesclou %[1]d commits de %[2]s em %[3]s %[4]s" + }, + "title_desc": { + "one": "quer mesclar %[1]d commit de %[2]s em %[3]s", + "other": "quer mesclar %[1]d commits de %[2]s em %[3]s" + } + } + }, + "search": { + "milestone_kind": "Pesquisar marcos..." + } +} diff --git a/options/locale_next/locale_pt-PT.json b/options/locale_next/locale_pt-PT.json new file mode 100644 index 0000000000..475023d461 --- /dev/null +++ b/options/locale_next/locale_pt-PT.json @@ -0,0 +1,17 @@ +{ + "repo": { + "pulls": { + "merged_title_desc": { + "one": "integrou %[1]d cometimento do ramo %[2]s no ramo %[3]s %[4]s", + "other": "integrou %[1]d cometimento(s) do ramo %[2]s no ramo %[3]s %[4]s" + }, + "title_desc": { + "one": "quer integrar %[1]d cometimento do ramo %[2]s no ramo %[3]s", + "other": "quer integrar %[1]d cometimento(s) do ramo %[2]s no ramo %[3]s" + } + } + }, + "search": { + "milestone_kind": "Procurar etapas..." + } +} diff --git a/options/locale_next/locale_ru-RU.json b/options/locale_next/locale_ru-RU.json new file mode 100644 index 0000000000..de310505a3 --- /dev/null +++ b/options/locale_next/locale_ru-RU.json @@ -0,0 +1,17 @@ +{ + "repo": { + "pulls": { + "merged_title_desc": { + "one": "слит %[1]d коммит из %[2]s в %[3]s %[4]s", + "many": "слито %[1]d коммит(ов) из %[2]s в %[3]s %[4]s" + }, + "title_desc": { + "one": "хочет влить %[1]d коммит из %[2]s в %[3]s", + "many": "хочет влить %[1]d коммит(ов) из %[2]s в %[3]s" + } + } + }, + "search": { + "milestone_kind": "Найти этапы..." + } +} diff --git a/options/locale_next/locale_si-LK.json b/options/locale_next/locale_si-LK.json new file mode 100644 index 0000000000..25e149f9b8 --- /dev/null +++ b/options/locale_next/locale_si-LK.json @@ -0,0 +1,12 @@ +{ + "repo": { + "pulls": { + "merged_title_desc": { + "other": "මර්ජ්%[1]d සිට %[2]s දක්වා %[3]s %[4]s" + }, + "title_desc": { + "other": "%[1]d සිට %[2]s දක්වා %[3]s" + } + } + } +} diff --git a/options/locale_next/locale_sk-SK.json b/options/locale_next/locale_sk-SK.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/options/locale_next/locale_sk-SK.json @@ -0,0 +1 @@ +{} diff --git a/options/locale_next/locale_sl.json b/options/locale_next/locale_sl.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/options/locale_next/locale_sl.json @@ -0,0 +1 @@ +{} diff --git a/options/locale_next/locale_sr-SP.json b/options/locale_next/locale_sr-SP.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/options/locale_next/locale_sr-SP.json @@ -0,0 +1 @@ +{} diff --git a/options/locale_next/locale_sv-SE.json b/options/locale_next/locale_sv-SE.json new file mode 100644 index 0000000000..fe68f161c2 --- /dev/null +++ b/options/locale_next/locale_sv-SE.json @@ -0,0 +1,15 @@ +{ + "repo": { + "pulls": { + "merged_title_desc": { + "other": "sammanfogade %[1]d incheckningar från %[2]s in i %[3]s %[4]s" + }, + "title_desc": { + "other": "vill sammanfoga %[1]d incheckningar från s[2]s in i %[3]s" + } + } + }, + "search": { + "milestone_kind": "Sök milstolpar..." + } +} diff --git a/options/locale_next/locale_tr-TR.json b/options/locale_next/locale_tr-TR.json new file mode 100644 index 0000000000..ef2cdb6584 --- /dev/null +++ b/options/locale_next/locale_tr-TR.json @@ -0,0 +1,15 @@ +{ + "repo": { + "pulls": { + "merged_title_desc": { + "other": "%[4]s %[2]s içindeki %[1]d işlemeyi %[3]s ile birleştirdi" + }, + "title_desc": { + "other": "%[2]s içindeki %[1]d işlemeyi %[3]s ile birleştirmek istiyor" + } + } + }, + "search": { + "milestone_kind": "Kilometre taşlarını ara..." + } +} diff --git a/options/locale_next/locale_uk-UA.json b/options/locale_next/locale_uk-UA.json new file mode 100644 index 0000000000..aded9786b4 --- /dev/null +++ b/options/locale_next/locale_uk-UA.json @@ -0,0 +1,17 @@ +{ + "repo": { + "pulls": { + "merged_title_desc": { + "one": "об'єднав %[1]d коміт з %[2]s в %[3]s %[4]s", + "many": "об'єднав %[1]d комітів з %[2]s в %[3]s %[4]s" + }, + "title_desc": { + "one": "хоче об'єднати %[1]d коміт з %[2]s в %[3]s", + "many": "хоче об'єднати %[1]d комітів з %[2]s в %[3]s" + } + } + }, + "search": { + "milestone_kind": "Шукати віхи..." + } +} diff --git a/options/locale_next/locale_vi.json b/options/locale_next/locale_vi.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/options/locale_next/locale_vi.json @@ -0,0 +1 @@ +{} diff --git a/options/locale_next/locale_yi.json b/options/locale_next/locale_yi.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/options/locale_next/locale_yi.json @@ -0,0 +1 @@ +{} diff --git a/options/locale_next/locale_zh-CN.json b/options/locale_next/locale_zh-CN.json new file mode 100644 index 0000000000..091b3fe609 --- /dev/null +++ b/options/locale_next/locale_zh-CN.json @@ -0,0 +1,15 @@ +{ + "repo": { + "pulls": { + "merged_title_desc": { + "other": "于 %[4]s 将 %[1]d 次代码提交从 %[2]s合并至 %[3]s" + }, + "title_desc": { + "other": "请求将 %[1]d 次代码提交从 %[2]s 合并至 %[3]s" + } + } + }, + "search": { + "milestone_kind": "搜索里程碑…" + } +} diff --git a/options/locale_next/locale_zh-HK.json b/options/locale_next/locale_zh-HK.json new file mode 100644 index 0000000000..dd7b954559 --- /dev/null +++ b/options/locale_next/locale_zh-HK.json @@ -0,0 +1,9 @@ +{ + "repo": { + "pulls": { + "merged_title_desc": { + "other": "於 %[4]s 將 %[1]d 次代碼提交從 %[2]s合併至 %[3]s" + } + } + } +} diff --git a/options/locale_next/locale_zh-TW.json b/options/locale_next/locale_zh-TW.json new file mode 100644 index 0000000000..4d31a713c0 --- /dev/null +++ b/options/locale_next/locale_zh-TW.json @@ -0,0 +1,15 @@ +{ + "repo": { + "pulls": { + "merged_title_desc": { + "other": "將 %[1]d 次提交從 %[2]s 合併至 %[3]s %[4]s" + }, + "title_desc": { + "other": "請求將 %[1]d 次程式碼提交從 %[2]s 合併至 %[3]s" + } + } + }, + "search": { + "milestone_kind": "搜尋里程碑..." + } +} diff --git a/templates/repo/issue/view_title.tmpl b/templates/repo/issue/view_title.tmpl index 5e30cf3684..936df9d3d2 100644 --- a/templates/repo/issue/view_title.tmpl +++ b/templates/repo/issue/view_title.tmpl @@ -63,10 +63,10 @@ {{$mergedStr:= DateUtils.TimeSince .Issue.PullRequest.MergedUnix}} {{if .Issue.OriginalAuthor}} {{.Issue.OriginalAuthor}} - {{ctx.Locale.TrN .NumCommits "repo.pulls.merged_title_desc_one" "repo.pulls.merged_title_desc_few" .NumCommits $headHref $baseHref $mergedStr}} + {{ctx.Locale.TrPluralString .NumCommits "repo.pulls.merged_title_desc" .NumCommits $headHref $baseHref $mergedStr}} {{else}} {{.Issue.PullRequest.Merger.GetDisplayName}} - {{ctx.Locale.TrN .NumCommits "repo.pulls.merged_title_desc_one" "repo.pulls.merged_title_desc_few" .NumCommits $headHref $baseHref $mergedStr}} + {{ctx.Locale.TrPluralString .NumCommits "repo.pulls.merged_title_desc" .NumCommits $headHref $baseHref $mergedStr}} {{end}} {{if .MadeUsingAGit}} {{/* TODO: Move documentation link to the instructions at the bottom of the PR, show instructions when clicking label */}} @@ -79,11 +79,11 @@ {{end}} {{else}} {{if .Issue.OriginalAuthor}} - {{.Issue.OriginalAuthor}} {{ctx.Locale.TrN .NumCommits "repo.pulls.title_desc_one" "repo.pulls.title_desc_few" .NumCommits $headHref $baseHref "branch_target"}} + {{.Issue.OriginalAuthor}} {{ctx.Locale.TrPluralString .NumCommits "repo.pulls.title_desc" .NumCommits $headHref $baseHref "branch_target"}} {{else}} {{.Issue.Poster.GetDisplayName}} - {{ctx.Locale.TrN .NumCommits "repo.pulls.title_desc_one" "repo.pulls.title_desc_few" .NumCommits $headHref $baseHref "branch_target"}} + {{ctx.Locale.TrPluralString .NumCommits "repo.pulls.title_desc" .NumCommits $headHref $baseHref "branch_target"}} {{end}} {{if .MadeUsingAGit}} diff --git a/tools/migrate_locales.sh b/tools/migrate_locales.sh new file mode 100755 index 0000000000..f02fe702cc --- /dev/null +++ b/tools/migrate_locales.sh @@ -0,0 +1,145 @@ +#!/bin/bash + +# Copyright 2024 The Forgejo Authors. All rights reserved. +# SPDX-License-Identifier: MIT + +if [ -z "$1" ] || [ -z "$2" ] +then + echo "USAGE: $0 section key [key1 [keyN]]" + exit 1 +fi + +if ! [ -d ../options/locale_next ] +then + echo 'Call this script from the `tools` directory.' + exit 1 +fi + +destsection="$1" +keyJSON="$destsection.$2" +key1="" +keyN="" +if [ -n "$3" ] +then + key1="$3" +else + key1="$2" +fi +if [ -n "$4" ] +then + keyN="$4" +fi + +cd ../options/locale + +# Migrate the string in one file. +function process() { + file="$1" + exec 3<$file + + val1="" + valN="" + cursection="" + line1=0 + lineN=0 + lineNumber=0 + + # Parse the file + while read -u 3 line + do + ((++lineNumber)) + if [[ $line =~ ^\[[-._a-zA-Z0-9]+\]$ ]] + then + cursection="${line#[}" + cursection="${cursection%]}" + elif [ "$cursection" = "$destsection" ] + then + key="${line%%=*}" + value="${line#*=}" + key="$(echo $key)" # Trim leading/trailing whitespace + value="$(echo $value)" + + if [ "$key" = "$key1" ] + then + val1="$value" + line1=$lineNumber + fi + if [ -n "$keyN" ] && [ "$key" = "$keyN" ] + then + valN="$value" + lineN=$lineNumber + fi + + if [ -n "$val1" ] && ( [ -n "$valN" ] || [ -z "$keyN" ] ) + then + # Found all desired strings + break + fi + fi + done + + if [ -n "$val1" ] || [ -n "$valN" ] + then + localename="${file#locale_}" + localename="${localename%.ini}" + localename="${localename%-*}" + + if [ "$file" = "locale_en-US.ini" ] + then + # Delete migrated string from source file + if [ $line1 -gt 0 ] && [ $lineN -gt 0 ] && [ $lineN -ne $line1 ] + then + sed -i "${line1}d;${lineN}d" "$file" + elif [ $line1 -gt 0 ] + then + sed -i "${line1}d" "$file" + elif [ $lineN -gt 0 ] + then + sed -i "${lineN}d" "$file" + fi + fi + + # Write JSON + jsonfile="../locale_next/${file/.ini/.json}" + + pluralform="other" + oneform="one" + case $localename in + "be" | "bs" | "cnr" | "csb" | "hr" | "lt" | "pl" | "ru" | "sr" | "szl" | "uk" | "wen") + # These languages have no "other" form and use "many" instead. + pluralform="many" + ;; + "ace" | "ay" | "bm" | "bo" | "cdo" | "cpx" | "crh" | "dz" | "gan" | "hak" | "hnj" | "hsn" | "id" | "ig" | "ii" | "ja" | "jbo" | "jv" | "kde" | "kea" | "km" | "ko" | "kos" | "lkt" | "lo" | "lzh" | "ms" | "my" | "nan" | "nqo" | "osa" | "sah" | "ses" | "sg" | "son" | "su" | "th" | "tlh" | "to" | "tok" | "tpi" | "tt" | "vi" | "wo" | "wuu" | "yo" | "yue" | "zh") + # These languages have no singular form. + oneform="" + ;; + *) + ;; + esac + + content="" + if [ -z "$keyN" ] + then + content="$(jq --arg val "$val1" ".$keyJSON = \$val" < "$jsonfile")" + else + object='{}' + if [ -n "$val1" ] && [ -n "$oneform" ] + then + object=$(jq --arg val "$val1" ".$oneform = \$val" <<< "$object") + fi + if [ -n "$valN" ] + then + object=$(jq --arg val "$valN" ".$pluralform = \$val" <<< "$object") + fi + content="$(jq --argjson val "$object" ".$keyJSON = \$val" < "$jsonfile")" + fi + jq . <<< "$content" > "$jsonfile" + fi +} + +for file in *.ini +do + process "$file" & +done +wait + From 34e1100ae22ef4fe70bace46ffa954aba85a0620 Mon Sep 17 00:00:00 2001 From: Beowulf Date: Fri, 17 Jan 2025 20:14:28 +0000 Subject: [PATCH 13/23] fix(ui): reset content of text field for comments when cancelling (#6595) Currently, the content of the text field is not reset when you cancel editing. This change resets the content of the text field when editing is canceled. If this is not done and you click on cancel and then on edit again, you can no longer return to the initial content without completely reloading the page. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/6595 Reviewed-by: Otto Reviewed-by: Gusted Co-authored-by: Beowulf Co-committed-by: Beowulf --- tests/e2e/issue-comment.test.e2e.ts | 21 +++++++++++++++++++++ web_src/js/features/repo-legacy.js | 1 + 2 files changed, 22 insertions(+) diff --git a/tests/e2e/issue-comment.test.e2e.ts b/tests/e2e/issue-comment.test.e2e.ts index 933e65fa32..1c19f98c48 100644 --- a/tests/e2e/issue-comment.test.e2e.ts +++ b/tests/e2e/issue-comment.test.e2e.ts @@ -77,6 +77,27 @@ test('Always focus edit tab first on edit', async ({page}) => { await save_visual(page); }); +test('Reset content of comment edit field on cancel', async ({page}) => { + const response = await page.goto('/user2/repo1/issues/1'); + expect(response?.status()).toBe(200); + + const editorTextarea = page.locator('[id="_combo_markdown_editor_1"]'); + + // Change the content of the edit field + await page.click('#issue-1 .comment-container .context-menu'); + await page.click('#issue-1 .comment-container .menu>.edit-content'); + await expect(editorTextarea).toHaveValue('content for the first issue'); + await editorTextarea.fill('some random string'); + await expect(editorTextarea).toHaveValue('some random string'); + await page.click('#issue-1 .comment-container .edit .cancel'); + + // Edit again and assert that the edit field should be reset to the initial content + await page.click('#issue-1 .comment-container .context-menu'); + await page.click('#issue-1 .comment-container .menu>.edit-content'); + await expect(editorTextarea).toHaveValue('content for the first issue'); + await save_visual(page); +}); + test('Quote reply', async ({page}, workerInfo) => { test.skip(workerInfo.project.name !== 'firefox', 'Uses Firefox specific selection quirks'); const response = await page.goto('/user2/repo1/issues/1'); diff --git a/web_src/js/features/repo-legacy.js b/web_src/js/features/repo-legacy.js index 04b6e93b39..c74ba1efbe 100644 --- a/web_src/js/features/repo-legacy.js +++ b/web_src/js/features/repo-legacy.js @@ -405,6 +405,7 @@ async function onEditContent(event) { e.preventDefault(); showElem(renderContent); hideElem(editContentZone); + comboMarkdownEditor.value(rawContent.textContent); comboMarkdownEditor.attachedDropzoneInst?.emit('reload'); }; From 5c8db434470d027d506d987cb5bf6c5641cb5fb7 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Sat, 18 Jan 2025 05:30:16 +0000 Subject: [PATCH 14/23] Update dependency katex to v0.16.21 (forgejo) (#6603) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | [katex](https://katex.org) ([source](https://github.com/KaTeX/KaTeX)) | dependencies | patch | [`0.16.20` -> `0.16.21`](https://renovatebot.com/diffs/npm/katex/0.16.20/0.16.21) | --- ### Release Notes
KaTeX/KaTeX (katex) ### [`v0.16.21`](https://github.com/KaTeX/KaTeX/blob/HEAD/CHANGELOG.md#01621-2025-01-17) [Compare Source](https://github.com/KaTeX/KaTeX/compare/v0.16.20...v0.16.21) ##### Bug Fixes - escape \htmlData attribute name ([57914ad](https://github.com/KaTeX/KaTeX/commit/57914ad91eff401357f44bf364b136d37eba04f8))
--- ### Configuration 📅 **Schedule**: Branch creation - "* 0-3 * * *" (UTC), Automerge - "* 0-3 * * *" (UTC). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/6603 Reviewed-by: Gusted Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9dbfaf2551..78e8494442 100644 --- a/package-lock.json +++ b/package-lock.json @@ -30,7 +30,7 @@ "htmx.org": "1.9.12", "idiomorph": "0.3.0", "jquery": "3.7.1", - "katex": "0.16.20", + "katex": "0.16.21", "mermaid": "11.4.1", "mini-css-extract-plugin": "2.9.2", "minimatch": "10.0.1", @@ -10402,9 +10402,9 @@ "license": "MIT" }, "node_modules/katex": { - "version": "0.16.20", - "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.20.tgz", - "integrity": "sha512-jjuLaMGD/7P8jUTpdKhA9IoqnH+yMFB3sdAFtq5QdAqeP2PjiSbnC3EaguKPNtv6dXXanHxp1ckwvF4a86LBig==", + "version": "0.16.21", + "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.21.tgz", + "integrity": "sha512-XvqR7FgOHtWupfMiigNzmh+MgUVmDGU2kXZm899ZkPfcuoPuFxyHmXsgATDpFZDAXCI8tvinaVcDo8PIIJSo4A==", "funding": [ "https://opencollective.com/katex", "https://github.com/sponsors/katex" diff --git a/package.json b/package.json index 944789f0a7..259a744c2b 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ "htmx.org": "1.9.12", "idiomorph": "0.3.0", "jquery": "3.7.1", - "katex": "0.16.20", + "katex": "0.16.21", "mermaid": "11.4.1", "mini-css-extract-plugin": "2.9.2", "minimatch": "10.0.1", From dbec2ed3507e5b84b9a3d39ffe9e52f2c4ebe7fd Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Sat, 18 Jan 2025 05:31:17 +0000 Subject: [PATCH 15/23] Update module github.com/caddyserver/certmagic to v0.21.7 (forgejo) (#6604) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | [github.com/caddyserver/certmagic](https://github.com/caddyserver/certmagic) | require | patch | `v0.21.6` -> `v0.21.7` | --- ### Release Notes
caddyserver/certmagic (github.com/caddyserver/certmagic) ### [`v0.21.7`](https://github.com/caddyserver/certmagic/compare/v0.21.6...v0.21.7) [Compare Source](https://github.com/caddyserver/certmagic/compare/v0.21.6...v0.21.7)
--- ### Configuration 📅 **Schedule**: Branch creation - "* 0-3 * * *" (UTC), Automerge - "* 0-3 * * *" (UTC). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/6604 Reviewed-by: Gusted Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 48e9e49075..e6d34f571d 100644 --- a/go.mod +++ b/go.mod @@ -27,7 +27,7 @@ require ( github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb github.com/blevesearch/bleve/v2 v2.4.4 github.com/buildkite/terminal-to-html/v3 v3.16.4 - github.com/caddyserver/certmagic v0.21.6 + github.com/caddyserver/certmagic v0.21.7 github.com/chi-middleware/proxy v1.1.1 github.com/djherbis/buffer v1.2.0 github.com/djherbis/nio/v3 v3.0.1 @@ -221,7 +221,7 @@ require ( github.com/markbates/going v1.0.3 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-runewidth v0.0.16 // indirect - github.com/mholt/acmez/v3 v3.0.0 // indirect + github.com/mholt/acmez/v3 v3.0.1 // indirect github.com/miekg/dns v1.1.62 // indirect github.com/minio/md5-simd v1.1.2 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect diff --git a/go.sum b/go.sum index 28e1f22870..7a74006088 100644 --- a/go.sum +++ b/go.sum @@ -770,8 +770,8 @@ github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= github.com/buildkite/terminal-to-html/v3 v3.16.4 h1:QFYO8IGvRnp7tGgiQb8g9uFU8kY9wOzxsFFx17+yy6Q= github.com/buildkite/terminal-to-html/v3 v3.16.4/go.mod h1:r/J7cC9c3EzBzP3/wDz0RJLPwv5PUAMp+KF2w+ntMc0= -github.com/caddyserver/certmagic v0.21.6 h1:1th6GfprVfsAtFNOu4StNMF5IxK5XiaI0yZhAHlZFPE= -github.com/caddyserver/certmagic v0.21.6/go.mod h1:n1sCo7zV1Ez2j+89wrzDxo4N/T1Ws/Vx8u5NvuBFabw= +github.com/caddyserver/certmagic v0.21.7 h1:66KJioPFJwttL43KYSWk7ErSmE6LfaJgCQuhm8Sg6fg= +github.com/caddyserver/certmagic v0.21.7/go.mod h1:LCPG3WLxcnjVKl/xpjzM0gqh0knrKKKiO5WVttX2eEI= github.com/caddyserver/zerossl v0.1.3 h1:onS+pxp3M8HnHpN5MMbOMyNjmTheJyWRaZYwn+YTAyA= github.com/caddyserver/zerossl v0.1.3/go.mod h1:CxA0acn7oEGO6//4rtrRjYgEoa4MFw/XofZnrYwGqG4= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= @@ -1253,8 +1253,8 @@ github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBW github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/meilisearch/meilisearch-go v0.29.0 h1:HZ9NEKN59USINQ/DXJge/aaXq8IrsKbXGTdAoBaaDz4= github.com/meilisearch/meilisearch-go v0.29.0/go.mod h1:2cRCAn4ddySUsFfNDLVPod/plRibQsJkXF/4gLhxbOk= -github.com/mholt/acmez/v3 v3.0.0 h1:r1NcjuWR0VaKP2BTjDK9LRFBw/WvURx3jlaEUl9Ht8E= -github.com/mholt/acmez/v3 v3.0.0/go.mod h1:L1wOU06KKvq7tswuMDwKdcHeKpFFgkppZy/y0DFxagQ= +github.com/mholt/acmez/v3 v3.0.1 h1:4PcjKjaySlgXK857aTfDuRbmnM5gb3Ruz3tvoSJAUp8= +github.com/mholt/acmez/v3 v3.0.1/go.mod h1:L1wOU06KKvq7tswuMDwKdcHeKpFFgkppZy/y0DFxagQ= github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk= github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA= github.com/miekg/dns v1.1.62 h1:cN8OuEF1/x5Rq6Np+h1epln8OiyPWV+lROx9LxcGgIQ= From 401906b88e945727d0980f428c026a148c503630 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Sat, 18 Jan 2025 05:33:34 +0000 Subject: [PATCH 16/23] Update module google.golang.org/protobuf to v1.36.3 (forgejo) (#6581) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | [google.golang.org/protobuf](https://github.com/protocolbuffers/protobuf-go) | require | patch | `v1.36.2` -> `v1.36.3` | --- ### Release Notes
protocolbuffers/protobuf-go (google.golang.org/protobuf) ### [`v1.36.3`](https://github.com/protocolbuffers/protobuf-go/releases/tag/v1.36.3) [Compare Source](https://github.com/protocolbuffers/protobuf-go/compare/v1.36.2...v1.36.3) **Full Changelog**: https://github.com/protocolbuffers/protobuf-go/compare/v1.36.2...v1.36.3 Bug fixes: [CL/642575](https://go-review.googlesource.com/c/protobuf/+/642575): reflect/protodesc: fix panic when working with dynamicpb [CL/641036](https://go-review.googlesource.com/c/protobuf/+/641036): cmd/protoc-gen-go: remove json struct tags from unexported fields User-visible changes: [CL/641876](https://go-review.googlesource.com/c/protobuf/+/641876): proto: add example for GetExtension, SetExtension [CL/642015](https://go-review.googlesource.com/c/protobuf/+/642015): runtime/protolazy: replace internal doc link with external link Maintenance: [CL/641635](https://go-review.googlesource.com/c/protobuf/+/641635): all: split flags.ProtoLegacyWeak out of flags.ProtoLegacy [CL/641019](https://go-review.googlesource.com/c/protobuf/+/641019): internal/impl: remove unused exporter parameter [CL/641018](https://go-review.googlesource.com/c/protobuf/+/641018): internal/impl: switch to reflect.Value.IsZero [CL/641035](https://go-review.googlesource.com/c/protobuf/+/641035): internal/impl: clean up unneeded Go<1.12 MapRange() alternative [CL/641017](https://go-review.googlesource.com/c/protobuf/+/641017): types/dynamicpb: switch atomicExtFiles to atomic.Uint64 type
--- ### Configuration 📅 **Schedule**: Branch creation - "* 0-3 * * *" (UTC), Automerge - "* 0-3 * * *" (UTC). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/6581 Reviewed-by: Gusted Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index e6d34f571d..bc93365ff2 100644 --- a/go.mod +++ b/go.mod @@ -109,7 +109,7 @@ require ( golang.org/x/sys v0.29.0 golang.org/x/text v0.21.0 google.golang.org/grpc v1.69.2 - google.golang.org/protobuf v1.36.2 + google.golang.org/protobuf v1.36.3 gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df gopkg.in/ini.v1 v1.67.0 gopkg.in/yaml.v3 v3.0.1 diff --git a/go.sum b/go.sum index 7a74006088..3064c26ba9 100644 --- a/go.sum +++ b/go.sum @@ -2179,8 +2179,8 @@ google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqw google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.29.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.36.2 h1:R8FeyR1/eLmkutZOM5CWghmo5itiG9z0ktFlTVLuTmU= -google.golang.org/protobuf v1.36.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/protobuf v1.36.3 h1:82DV7MYdb8anAVi3qge1wSnMDrnKK7ebr+I0hHRN1BU= +google.golang.org/protobuf v1.36.3/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk= gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= From f1a92de4e6a78abf3fb639587662ec01f82250df Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Sat, 18 Jan 2025 06:39:17 +0000 Subject: [PATCH 17/23] Update postcss (forgejo) (#6562) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | [postcss](https://postcss.org/) ([source](https://github.com/postcss/postcss)) | dependencies | minor | [`8.4.49` -> `8.5.1`](https://renovatebot.com/diffs/npm/postcss/8.4.49/8.5.1) | | [postcss-html](https://github.com/ota-meshi/postcss-html) | devDependencies | minor | [`1.7.0` -> `1.8.0`](https://renovatebot.com/diffs/npm/postcss-html/1.7.0/1.8.0) | --- ### Release Notes
postcss/postcss (postcss) ### [`v8.5.1`](https://github.com/postcss/postcss/blob/HEAD/CHANGELOG.md#851) [Compare Source](https://github.com/postcss/postcss/compare/8.5.0...8.5.1) - Fixed backwards compatibility for complex cases (by [@​romainmenke](https://github.com/romainmenke)). ### [`v8.5.0`](https://github.com/postcss/postcss/releases/tag/8.5.0): 8.5 “Duke Alloces” [Compare Source](https://github.com/postcss/postcss/compare/8.4.49...8.5.0) President Alloces seal PostCSS 8.5 brought API to work better with non-CSS sources like HTML, Vue.js/Svelte sources or CSS-in-JS. [@​romainmenke](https://github.com/romainmenke) during [his work](https://github.com/postcss/postcss/issues/1995) on [Stylelint](https://stylelint.io) added `Input#document` in additional to `Input#css`. ```js root.source.input.document //=> "

Hello

// " root.source.input.css //=> "p { // color: green; // }" ``` #### Thanks to Sponsors This release was possible thanks to our community. If your company wants to support the sustainability of front-end infrastructure or wants to give some love to PostCSS, you can join our supporters by: - [**Tidelift**](https://tidelift.com/) with a Spotify-like subscription model supporting all projects from your lock file. - Direct donations at [**GitHub Sponsors**](https://github.com/sponsors/ai) or [**Open Collective**](https://opencollective.com/postcss#section-contributors).
ota-meshi/postcss-html (postcss-html) ### [`v1.8.0`](https://github.com/ota-meshi/postcss-html/releases/tag/v1.8.0) [Compare Source](https://github.com/ota-meshi/postcss-html/compare/v1.7.0...v1.8.0) #### What's Changed - update to latest PostCSS by [@​romainmenke](https://github.com/romainmenke) in https://github.com/ota-meshi/postcss-html/pull/134 **Full Changelog**: https://github.com/ota-meshi/postcss-html/compare/v1.7.0...v1.8.0
--- ### Configuration 📅 **Schedule**: Branch creation - "* 0-3 * * *" (UTC), Automerge - "* 0-3 * * *" (UTC). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 👻 **Immortal**: This PR will be recreated if closed unmerged. Get [config help](https://github.com/renovatebot/renovate/discussions) if that's undesired. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/6562 Reviewed-by: Gusted Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- package-lock.json | 20 ++++++++++---------- package.json | 4 ++-- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/package-lock.json b/package-lock.json index 78e8494442..445721e70f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -37,7 +37,7 @@ "monaco-editor": "0.52.2", "monaco-editor-webpack-plugin": "7.1.0", "pdfobject": "2.3.0", - "postcss": "8.4.49", + "postcss": "8.5.1", "postcss-loader": "8.1.1", "postcss-nesting": "13.0.1", "pretty-ms": "9.0.0", @@ -89,7 +89,7 @@ "happy-dom": "16.3.0", "license-checker-rseidelsohn": "4.4.2", "markdownlint-cli": "0.43.0", - "postcss-html": "1.7.0", + "postcss-html": "1.8.0", "stylelint": "16.12.0", "stylelint-declaration-block-no-ignored-properties": "2.8.0", "stylelint-declaration-strict-value": "1.10.6", @@ -11867,9 +11867,9 @@ } }, "node_modules/postcss": { - "version": "8.4.49", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz", - "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==", + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.1.tgz", + "integrity": "sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ==", "funding": [ { "type": "opencollective", @@ -11886,7 +11886,7 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.7", + "nanoid": "^3.3.8", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, @@ -11895,15 +11895,15 @@ } }, "node_modules/postcss-html": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/postcss-html/-/postcss-html-1.7.0.tgz", - "integrity": "sha512-MfcMpSUIaR/nNgeVS8AyvyDugXlADjN9AcV7e5rDfrF1wduIAGSkL4q2+wgrZgA3sHVAHLDO9FuauHhZYW2nBw==", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/postcss-html/-/postcss-html-1.8.0.tgz", + "integrity": "sha512-5mMeb1TgLWoRKxZ0Xh9RZDfwUUIqRrcxO2uXO+Ezl1N5lqpCiSU5Gk6+1kZediBfBHFtPCdopr2UZ2SgUsKcgQ==", "dev": true, "license": "MIT", "dependencies": { "htmlparser2": "^8.0.0", "js-tokens": "^9.0.0", - "postcss": "^8.4.0", + "postcss": "^8.5.0", "postcss-safe-parser": "^6.0.0" }, "engines": { diff --git a/package.json b/package.json index 259a744c2b..95129444fb 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,7 @@ "monaco-editor": "0.52.2", "monaco-editor-webpack-plugin": "7.1.0", "pdfobject": "2.3.0", - "postcss": "8.4.49", + "postcss": "8.5.1", "postcss-loader": "8.1.1", "postcss-nesting": "13.0.1", "pretty-ms": "9.0.0", @@ -88,7 +88,7 @@ "happy-dom": "16.3.0", "license-checker-rseidelsohn": "4.4.2", "markdownlint-cli": "0.43.0", - "postcss-html": "1.7.0", + "postcss-html": "1.8.0", "stylelint": "16.12.0", "stylelint-declaration-block-no-ignored-properties": "2.8.0", "stylelint-declaration-strict-value": "1.10.6", From 244ee64c30ceee31ee43ba7c9b074a0711ace5e8 Mon Sep 17 00:00:00 2001 From: 0ko <0ko@noreply.codeberg.org> Date: Sat, 18 Jan 2025 07:35:28 +0000 Subject: [PATCH 18/23] fix(i18n): flatten next locales (#6607) Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/6607 Reviewed-by: Gusted --- options/locale_next/locale_bg.json | 20 ++++++++------------ options/locale_next/locale_ca.json | 4 +--- options/locale_next/locale_cs-CZ.json | 24 +++++++++--------------- options/locale_next/locale_da.json | 4 +--- options/locale_next/locale_de-DE.json | 24 +++++++++--------------- options/locale_next/locale_el-GR.json | 24 +++++++++--------------- options/locale_next/locale_en-US.json | 24 +++++++++--------------- options/locale_next/locale_eo.json | 4 +--- options/locale_next/locale_es-ES.json | 24 +++++++++--------------- options/locale_next/locale_et.json | 4 +--- options/locale_next/locale_fa-IR.json | 16 ++++++---------- options/locale_next/locale_fi-FI.json | 20 +++++++------------- options/locale_next/locale_fil.json | 24 +++++++++--------------- options/locale_next/locale_fr-FR.json | 24 +++++++++--------------- options/locale_next/locale_hu-HU.json | 20 +++++++------------- options/locale_next/locale_id-ID.json | 16 ++++++---------- options/locale_next/locale_is-IS.json | 10 +++------- options/locale_next/locale_it-IT.json | 24 +++++++++--------------- options/locale_next/locale_ja-JP.json | 20 +++++++------------- options/locale_next/locale_ko-KR.json | 16 ++++++---------- options/locale_next/locale_lt.json | 4 +--- options/locale_next/locale_lv-LV.json | 24 +++++++++--------------- options/locale_next/locale_nds.json | 24 +++++++++--------------- options/locale_next/locale_nl-NL.json | 24 +++++++++--------------- options/locale_next/locale_pl-PL.json | 20 +++++++------------- options/locale_next/locale_pt-BR.json | 24 +++++++++--------------- options/locale_next/locale_pt-PT.json | 24 +++++++++--------------- options/locale_next/locale_ru-RU.json | 24 +++++++++--------------- options/locale_next/locale_si-LK.json | 16 ++++++---------- options/locale_next/locale_sv-SE.json | 20 +++++++------------- options/locale_next/locale_tr-TR.json | 20 +++++++------------- options/locale_next/locale_uk-UA.json | 24 +++++++++--------------- options/locale_next/locale_zh-CN.json | 20 +++++++------------- options/locale_next/locale_zh-HK.json | 10 +++------- options/locale_next/locale_zh-TW.json | 20 +++++++------------- 35 files changed, 234 insertions(+), 410 deletions(-) diff --git a/options/locale_next/locale_bg.json b/options/locale_next/locale_bg.json index bec72c556a..02144c8b38 100644 --- a/options/locale_next/locale_bg.json +++ b/options/locale_next/locale_bg.json @@ -1,14 +1,10 @@ { - "repo": { - "pulls": { - "merged_title_desc": { - "one": "сля %[1]d подаване от %[2]s в %[3]s %[4]s", - "other": "сля %[1]d подавания от %[2]s в %[3]s %[4]s" - }, - "title_desc": { - "one": "иска да слее %[1]d подаване от %[2]s в %[3]s", - "other": "иска да слее %[1]d подавания от %[2]s в %[3]s" - } - } - } + "repo.pulls.merged_title_desc": { + "one": "сля %[1]d подаване от %[2]s в %[3]s %[4]s", + "other": "сля %[1]d подавания от %[2]s в %[3]s %[4]s" + }, + "repo.pulls.title_desc": { + "one": "иска да слее %[1]d подаване от %[2]s в %[3]s", + "other": "иска да слее %[1]d подавания от %[2]s в %[3]s" + } } diff --git a/options/locale_next/locale_ca.json b/options/locale_next/locale_ca.json index 8aee80092d..c639c28a99 100644 --- a/options/locale_next/locale_ca.json +++ b/options/locale_next/locale_ca.json @@ -1,5 +1,3 @@ { - "search": { - "milestone_kind": "Cerca fites..." - } + "search.milestone_kind": "Cerca fites..." } diff --git a/options/locale_next/locale_cs-CZ.json b/options/locale_next/locale_cs-CZ.json index 373b9dc31e..8d028b8367 100644 --- a/options/locale_next/locale_cs-CZ.json +++ b/options/locale_next/locale_cs-CZ.json @@ -1,17 +1,11 @@ { - "repo": { - "pulls": { - "merged_title_desc": { - "one": "sloučil %[1]d commit z %[2]s do %[3]s %[4]s", - "other": "sloučil %[1]d commity z větve %[2]s do větve %[3]s před %[4]s" - }, - "title_desc": { - "one": "žádá o sloučení %[1]d commitu z %[2]s do %[3]s", - "other": "chce sloučit %[1]d commity z větve %[2]s do %[3]s" - } - } - }, - "search": { - "milestone_kind": "Hledat milníky..." - } + "repo.pulls.merged_title_desc": { + "one": "sloučil %[1]d commit z %[2]s do %[3]s %[4]s", + "other": "sloučil %[1]d commity z větve %[2]s do větve %[3]s před %[4]s" + }, + "repo.pulls.title_desc": { + "one": "žádá o sloučení %[1]d commitu z %[2]s do %[3]s", + "other": "chce sloučit %[1]d commity z větve %[2]s do %[3]s" + }, + "search.milestone_kind": "Hledat milníky..." } diff --git a/options/locale_next/locale_da.json b/options/locale_next/locale_da.json index 834f66024f..0c2c9a25ea 100644 --- a/options/locale_next/locale_da.json +++ b/options/locale_next/locale_da.json @@ -1,5 +1,3 @@ { - "search": { - "milestone_kind": "Søg milepæle..." - } + "search.milestone_kind": "Søg milepæle..." } diff --git a/options/locale_next/locale_de-DE.json b/options/locale_next/locale_de-DE.json index 82e4ea54d4..f4a15ecdb9 100644 --- a/options/locale_next/locale_de-DE.json +++ b/options/locale_next/locale_de-DE.json @@ -1,17 +1,11 @@ { - "repo": { - "pulls": { - "merged_title_desc": { - "one": "hat %[1]d Commit von %[2]s nach %[3]s %[4]s zusammengeführt", - "other": "hat %[1]d Commits von %[2]s nach %[3]s %[4]s zusammengeführt" - }, - "title_desc": { - "one": "möchte %[1]d Commit von %[2]s nach %[3]s zusammenführen", - "other": "möchte %[1]d Commits von %[2]s nach %[3]s zusammenführen" - } - } - }, - "search": { - "milestone_kind": "Meilensteine suchen …" - } + "repo.pulls.merged_title_desc": { + "one": "hat %[1]d Commit von %[2]s nach %[3]s %[4]s zusammengeführt", + "other": "hat %[1]d Commits von %[2]s nach %[3]s %[4]s zusammengeführt" + }, + "repo.pulls.title_desc": { + "one": "möchte %[1]d Commit von %[2]s nach %[3]s zusammenführen", + "other": "möchte %[1]d Commits von %[2]s nach %[3]s zusammenführen" + }, + "search.milestone_kind": "Meilensteine suchen …" } diff --git a/options/locale_next/locale_el-GR.json b/options/locale_next/locale_el-GR.json index 9fa112cf0f..54ee504201 100644 --- a/options/locale_next/locale_el-GR.json +++ b/options/locale_next/locale_el-GR.json @@ -1,17 +1,11 @@ { - "repo": { - "pulls": { - "merged_title_desc": { - "one": "συγχώνευσε %[1]d υποβολή από τον κλάδο %[2]s στον κλάδο %[3]s %[4]s", - "other": "συγχώνευσε %[1]d υποβολές από %[2]s σε %[3]s %[4]s" - }, - "title_desc": { - "one": ": θα ήθελε να συγχωνεύσει %[1]d υποβολή από τον κλάδο %[2]s στον κλάδο %[3]s", - "other": "θέλει να συγχωνεύσει %[1]d υποβολές από %[2]s σε %[3]s" - } - } - }, - "search": { - "milestone_kind": "Αναζήτηση ορόσημων..." - } + "repo.pulls.merged_title_desc": { + "one": "συγχώνευσε %[1]d υποβολή από τον κλάδο %[2]s στον κλάδο %[3]s %[4]s", + "other": "συγχώνευσε %[1]d υποβολές από %[2]s σε %[3]s %[4]s" + }, + "repo.pulls.title_desc": { + "one": ": θα ήθελε να συγχωνεύσει %[1]d υποβολή από τον κλάδο %[2]s στον κλάδο %[3]s", + "other": "θέλει να συγχωνεύσει %[1]d υποβολές από %[2]s σε %[3]s" + }, + "search.milestone_kind": "Αναζήτηση ορόσημων..." } diff --git a/options/locale_next/locale_en-US.json b/options/locale_next/locale_en-US.json index 64e3e50abc..f8b2bcd0f6 100644 --- a/options/locale_next/locale_en-US.json +++ b/options/locale_next/locale_en-US.json @@ -1,17 +1,11 @@ { - "repo": { - "pulls": { - "merged_title_desc": { - "one": "merged %[1]d commit from %[2]s into %[3]s %[4]s", - "other": "merged %[1]d commits from %[2]s into %[3]s %[4]s" - }, - "title_desc": { - "one": "wants to merge %[1]d commit from %[2]s into %[3]s", - "other": "wants to merge %[1]d commits from %[2]s into %[3]s" - } - } - }, - "search": { - "milestone_kind": "Search milestones..." - } + "repo.pulls.merged_title_desc": { + "one": "merged %[1]d commit from %[2]s into %[3]s %[4]s", + "other": "merged %[1]d commits from %[2]s into %[3]s %[4]s" + }, + "repo.pulls.title_desc": { + "one": "wants to merge %[1]d commit from %[2]s into %[3]s", + "other": "wants to merge %[1]d commits from %[2]s into %[3]s" + }, + "search.milestone_kind": "Search milestones..." } diff --git a/options/locale_next/locale_eo.json b/options/locale_next/locale_eo.json index c57b462d5f..2c76a8562b 100644 --- a/options/locale_next/locale_eo.json +++ b/options/locale_next/locale_eo.json @@ -1,5 +1,3 @@ { - "search": { - "milestone_kind": "Serĉi celojn..." - } + "search.milestone_kind": "Serĉi celojn..." } diff --git a/options/locale_next/locale_es-ES.json b/options/locale_next/locale_es-ES.json index 7313cdc0a7..e95e91f696 100644 --- a/options/locale_next/locale_es-ES.json +++ b/options/locale_next/locale_es-ES.json @@ -1,17 +1,11 @@ { - "repo": { - "pulls": { - "merged_title_desc": { - "one": "fusionó %[1]d commit de %[2]s en %[3]s %[4]s", - "other": "fusionó %[1]d commits de %[2]s en %[3]s %[4]s" - }, - "title_desc": { - "one": "quiere fusionar %[1]d commit de %[2]s en %[3]s", - "other": "quiere fusionar %[1]d commits de %[2]s en %[3]s" - } - } - }, - "search": { - "milestone_kind": "Buscar hitos…" - } + "repo.pulls.merged_title_desc": { + "one": "fusionó %[1]d commit de %[2]s en %[3]s %[4]s", + "other": "fusionó %[1]d commits de %[2]s en %[3]s %[4]s" + }, + "repo.pulls.title_desc": { + "one": "quiere fusionar %[1]d commit de %[2]s en %[3]s", + "other": "quiere fusionar %[1]d commits de %[2]s en %[3]s" + }, + "search.milestone_kind": "Buscar hitos…" } diff --git a/options/locale_next/locale_et.json b/options/locale_next/locale_et.json index ac009856db..f8846a15b8 100644 --- a/options/locale_next/locale_et.json +++ b/options/locale_next/locale_et.json @@ -1,5 +1,3 @@ { - "search": { - "milestone_kind": "Otsi verstapostid..." - } + "search.milestone_kind": "Otsi verstapostid..." } diff --git a/options/locale_next/locale_fa-IR.json b/options/locale_next/locale_fa-IR.json index 6964db4934..0a703d22d7 100644 --- a/options/locale_next/locale_fa-IR.json +++ b/options/locale_next/locale_fa-IR.json @@ -1,12 +1,8 @@ { - "repo": { - "pulls": { - "merged_title_desc": { - "other": "%[1]d کامیت ادغام شده از %[2]s به %[3]s %[4]s" - }, - "title_desc": { - "other": "قصد ادغام %[1]d تغییر را از %[2]s به %[3]s دارد" - } - } - } + "repo.pulls.merged_title_desc": { + "other": "%[1]d کامیت ادغام شده از %[2]s به %[3]s %[4]s" + }, + "repo.pulls.title_desc": { + "other": "قصد ادغام %[1]d تغییر را از %[2]s به %[3]s دارد" + } } diff --git a/options/locale_next/locale_fi-FI.json b/options/locale_next/locale_fi-FI.json index 88a2110bcb..c4c7e1f7dc 100644 --- a/options/locale_next/locale_fi-FI.json +++ b/options/locale_next/locale_fi-FI.json @@ -1,15 +1,9 @@ { - "repo": { - "pulls": { - "merged_title_desc": { - "other": "yhdistetty %[1]d committia lähteestä %[2]s kohteeseen %[3]s %[4]s" - }, - "title_desc": { - "other": "haluaa yhdistää %[1]d committia lähteestä %[2]s kohteeseen %[3]s" - } - } - }, - "search": { - "milestone_kind": "Etsi merkkipaaluja..." - } + "repo.pulls.merged_title_desc": { + "other": "yhdistetty %[1]d committia lähteestä %[2]s kohteeseen %[3]s %[4]s" + }, + "repo.pulls.title_desc": { + "other": "haluaa yhdistää %[1]d committia lähteestä %[2]s kohteeseen %[3]s" + }, + "search.milestone_kind": "Etsi merkkipaaluja..." } diff --git a/options/locale_next/locale_fil.json b/options/locale_next/locale_fil.json index 6be27dbb8c..9c57ef16cc 100644 --- a/options/locale_next/locale_fil.json +++ b/options/locale_next/locale_fil.json @@ -1,17 +1,11 @@ { - "repo": { - "pulls": { - "merged_title_desc": { - "one": "isinali ang %[1]d commit mula%[2]s patungong %[3]s %[4]s", - "other": "isinali ang %[1]d mga commit mula sa %[2]s patungong %[3]s %[4]s" - }, - "title_desc": { - "one": "hinihiling na isama ang %[1]d commit mula %[2]s patungong %[3]s", - "other": "hiniling na isama ang %[1]d mga commit mula sa %[2]s patungong %[3]s" - } - } - }, - "search": { - "milestone_kind": "Maghanap ng mga milestone…" - } + "repo.pulls.merged_title_desc": { + "one": "isinali ang %[1]d commit mula%[2]s patungong %[3]s %[4]s", + "other": "isinali ang %[1]d mga commit mula sa %[2]s patungong %[3]s %[4]s" + }, + "repo.pulls.title_desc": { + "one": "hinihiling na isama ang %[1]d commit mula %[2]s patungong %[3]s", + "other": "hiniling na isama ang %[1]d mga commit mula sa %[2]s patungong %[3]s" + }, + "search.milestone_kind": "Maghanap ng mga milestone…" } diff --git a/options/locale_next/locale_fr-FR.json b/options/locale_next/locale_fr-FR.json index 995a2be8bb..173b10de21 100644 --- a/options/locale_next/locale_fr-FR.json +++ b/options/locale_next/locale_fr-FR.json @@ -1,17 +1,11 @@ { - "repo": { - "pulls": { - "merged_title_desc": { - "one": "fusionné %[1]d commit depuis %[2]s vers %[3]s %[4]s", - "other": "a fusionné %[1]d révision(s) à partir de %[2]s vers %[3]s %[4]s" - }, - "title_desc": { - "one": "veut fusionner %[1]d commit depuis %[2]s vers %[3]s", - "other": "souhaite fusionner %[1]d révision(s) depuis %[2]s vers %[3]s" - } - } - }, - "search": { - "milestone_kind": "Recherche dans les jalons..." - } + "repo.pulls.merged_title_desc": { + "one": "fusionné %[1]d commit depuis %[2]s vers %[3]s %[4]s", + "other": "a fusionné %[1]d révision(s) à partir de %[2]s vers %[3]s %[4]s" + }, + "repo.pulls.title_desc": { + "one": "veut fusionner %[1]d commit depuis %[2]s vers %[3]s", + "other": "souhaite fusionner %[1]d révision(s) depuis %[2]s vers %[3]s" + }, + "search.milestone_kind": "Recherche dans les jalons..." } diff --git a/options/locale_next/locale_hu-HU.json b/options/locale_next/locale_hu-HU.json index 2a21d68095..60c8cfacd3 100644 --- a/options/locale_next/locale_hu-HU.json +++ b/options/locale_next/locale_hu-HU.json @@ -1,15 +1,9 @@ { - "repo": { - "pulls": { - "merged_title_desc": { - "other": "egyesítve %[1]d változás(ok) a %[2]s-ból %[3]s-ba %[4]s" - }, - "title_desc": { - "other": "egyesíteni szeretné %[1]d változás(oka)t a(z) %[2]s-ból %[3]s-ba" - } - } - }, - "search": { - "milestone_kind": "Mérföldkövek keresése..." - } + "repo.pulls.merged_title_desc": { + "other": "egyesítve %[1]d változás(ok) a %[2]s-ból %[3]s-ba %[4]s" + }, + "repo.pulls.title_desc": { + "other": "egyesíteni szeretné %[1]d változás(oka)t a(z) %[2]s-ból %[3]s-ba" + }, + "search.milestone_kind": "Mérföldkövek keresése..." } diff --git a/options/locale_next/locale_id-ID.json b/options/locale_next/locale_id-ID.json index 13580f3cfe..f2dac8114f 100644 --- a/options/locale_next/locale_id-ID.json +++ b/options/locale_next/locale_id-ID.json @@ -1,12 +1,8 @@ { - "repo": { - "pulls": { - "merged_title_desc": { - "other": "commit %[1]d telah digabungkan dari %[2]s menjadi %[3]s %[4]s" - }, - "title_desc": { - "other": "ingin menggabungkan komit %[1]d dari %[2]s menuju %[3]s" - } - } - } + "repo.pulls.merged_title_desc": { + "other": "commit %[1]d telah digabungkan dari %[2]s menjadi %[3]s %[4]s" + }, + "repo.pulls.title_desc": { + "other": "ingin menggabungkan komit %[1]d dari %[2]s menuju %[3]s" + } } diff --git a/options/locale_next/locale_is-IS.json b/options/locale_next/locale_is-IS.json index 40d5a7e7aa..a92d924232 100644 --- a/options/locale_next/locale_is-IS.json +++ b/options/locale_next/locale_is-IS.json @@ -1,9 +1,5 @@ { - "repo": { - "pulls": { - "title_desc": { - "other": "vill sameina %[1]d framlög frá %[2]s í %[3]s" - } - } - } + "repo.pulls.title_desc": { + "other": "vill sameina %[1]d framlög frá %[2]s í %[3]s" + } } diff --git a/options/locale_next/locale_it-IT.json b/options/locale_next/locale_it-IT.json index 61cb012433..ba90fa154f 100644 --- a/options/locale_next/locale_it-IT.json +++ b/options/locale_next/locale_it-IT.json @@ -1,17 +1,11 @@ { - "repo": { - "pulls": { - "merged_title_desc": { - "one": "ha fuso %[1]d commit da %[2]s in %[3]s %[4]s", - "other": "ha unito %[1]d commit da %[2]s a %[3]s %[4]s" - }, - "title_desc": { - "one": "vuole fondere %[1]d commit da %[2]s in %[3]s", - "other": "vuole unire %[1]d commit da %[2]s a %[3]s" - } - } - }, - "search": { - "milestone_kind": "Ricerca tappe..." - } + "repo.pulls.merged_title_desc": { + "one": "ha fuso %[1]d commit da %[2]s in %[3]s %[4]s", + "other": "ha unito %[1]d commit da %[2]s a %[3]s %[4]s" + }, + "repo.pulls.title_desc": { + "one": "vuole fondere %[1]d commit da %[2]s in %[3]s", + "other": "vuole unire %[1]d commit da %[2]s a %[3]s" + }, + "search.milestone_kind": "Ricerca tappe..." } diff --git a/options/locale_next/locale_ja-JP.json b/options/locale_next/locale_ja-JP.json index 447ee8ae22..f72d1a3fb6 100644 --- a/options/locale_next/locale_ja-JP.json +++ b/options/locale_next/locale_ja-JP.json @@ -1,15 +1,9 @@ { - "repo": { - "pulls": { - "merged_title_desc": { - "other": "が %[1]d 個のコミットを %[2]s から %[3]s へマージ %[4]s" - }, - "title_desc": { - "other": "が %[2]s から %[3]s への %[1]d コミットのマージを希望しています" - } - } - }, - "search": { - "milestone_kind": "マイルストーンを検索..." - } + "repo.pulls.merged_title_desc": { + "other": "が %[1]d 個のコミットを %[2]s から %[3]s へマージ %[4]s" + }, + "repo.pulls.title_desc": { + "other": "が %[2]s から %[3]s への %[1]d コミットのマージを希望しています" + }, + "search.milestone_kind": "マイルストーンを検索..." } diff --git a/options/locale_next/locale_ko-KR.json b/options/locale_next/locale_ko-KR.json index 1beaec4627..2acaca6084 100644 --- a/options/locale_next/locale_ko-KR.json +++ b/options/locale_next/locale_ko-KR.json @@ -1,12 +1,8 @@ { - "repo": { - "pulls": { - "merged_title_desc": { - "other": "님이 %[2]s 에서 %[3]s 로 %[1]d 커밋을 %[4]s 병합함" - }, - "title_desc": { - "other": "%[2]s 에서 %[3]s 로 %[1]d개의 커밋들을 병합하려함" - } - } - } + "repo.pulls.merged_title_desc": { + "other": "님이 %[2]s 에서 %[3]s 로 %[1]d 커밋을 %[4]s 병합함" + }, + "repo.pulls.title_desc": { + "other": "%[2]s 에서 %[3]s 로 %[1]d개의 커밋들을 병합하려함" + } } diff --git a/options/locale_next/locale_lt.json b/options/locale_next/locale_lt.json index cce5782546..d81780a2ab 100644 --- a/options/locale_next/locale_lt.json +++ b/options/locale_next/locale_lt.json @@ -1,5 +1,3 @@ { - "search": { - "milestone_kind": "Ieškoti gairių..." - } + "search.milestone_kind": "Ieškoti gairių..." } diff --git a/options/locale_next/locale_lv-LV.json b/options/locale_next/locale_lv-LV.json index e338556aab..a16e3aaf8a 100644 --- a/options/locale_next/locale_lv-LV.json +++ b/options/locale_next/locale_lv-LV.json @@ -1,17 +1,11 @@ { - "repo": { - "pulls": { - "merged_title_desc": { - "one": "iekļāva %[1]d iesūtījumu no %[2]s %[3]s %[4]s", - "other": "Iekļāva %[1]d iesūtījumus no %[2]s zarā %[3]s %[4]s" - }, - "title_desc": { - "one": "vēlas iekļaut %[1]d iesūtījumu no %[2]s %[3]s", - "other": "vēlas iekļaut %[1]d iesūtījumus no %[2]s zarā %[3]s" - } - } - }, - "search": { - "milestone_kind": "Meklēt atskaites punktus..." - } + "repo.pulls.merged_title_desc": { + "one": "iekļāva %[1]d iesūtījumu no %[2]s %[3]s %[4]s", + "other": "Iekļāva %[1]d iesūtījumus no %[2]s zarā %[3]s %[4]s" + }, + "repo.pulls.title_desc": { + "one": "vēlas iekļaut %[1]d iesūtījumu no %[2]s %[3]s", + "other": "vēlas iekļaut %[1]d iesūtījumus no %[2]s zarā %[3]s" + }, + "search.milestone_kind": "Meklēt atskaites punktus..." } diff --git a/options/locale_next/locale_nds.json b/options/locale_next/locale_nds.json index a141362816..564302820a 100644 --- a/options/locale_next/locale_nds.json +++ b/options/locale_next/locale_nds.json @@ -1,17 +1,11 @@ { - "repo": { - "pulls": { - "merged_title_desc": { - "one": "hett %[1]d Kommitteren vun %[2]s na %[3]s %[4]s tosamenföhrt", - "other": "hett %[1]d Kommitterens vun %[2]s na %[3]s %[4]s tosamenföhrt" - }, - "title_desc": { - "one": "will %[1]d Kommitteren vun %[2]s na %[3]s tosamenföhren", - "other": "will %[1]d Kommitterens vun %[2]s na %[3]s tosamenföhren" - } - } - }, - "search": { - "milestone_kind": "In Markstenen söken …" - } + "repo.pulls.merged_title_desc": { + "one": "hett %[1]d Kommitteren vun %[2]s na %[3]s %[4]s tosamenföhrt", + "other": "hett %[1]d Kommitterens vun %[2]s na %[3]s %[4]s tosamenföhrt" + }, + "repo.pulls.title_desc": { + "one": "will %[1]d Kommitteren vun %[2]s na %[3]s tosamenföhren", + "other": "will %[1]d Kommitterens vun %[2]s na %[3]s tosamenföhren" + }, + "search.milestone_kind": "In Markstenen söken …" } diff --git a/options/locale_next/locale_nl-NL.json b/options/locale_next/locale_nl-NL.json index 9bbfc0fecd..fbb78e9280 100644 --- a/options/locale_next/locale_nl-NL.json +++ b/options/locale_next/locale_nl-NL.json @@ -1,17 +1,11 @@ { - "repo": { - "pulls": { - "merged_title_desc": { - "one": "heeft %[1]d commit van %[2]s samengevoegd in %[3]s %[4]s", - "other": "heeft %[1]d commits samengevoegd van %[2]s naar %[3]s %[4]s" - }, - "title_desc": { - "one": "wilt %[1]d commit van %[2]s samenvoegen in %[3]s", - "other": "wilt %[1]d commits van %[2]s samenvoegen met %[3]s" - } - } - }, - "search": { - "milestone_kind": "Zoek mijlpalen..." - } + "repo.pulls.merged_title_desc": { + "one": "heeft %[1]d commit van %[2]s samengevoegd in %[3]s %[4]s", + "other": "heeft %[1]d commits samengevoegd van %[2]s naar %[3]s %[4]s" + }, + "repo.pulls.title_desc": { + "one": "wilt %[1]d commit van %[2]s samenvoegen in %[3]s", + "other": "wilt %[1]d commits van %[2]s samenvoegen met %[3]s" + }, + "search.milestone_kind": "Zoek mijlpalen..." } diff --git a/options/locale_next/locale_pl-PL.json b/options/locale_next/locale_pl-PL.json index 1f2def3ea0..2edcb573c4 100644 --- a/options/locale_next/locale_pl-PL.json +++ b/options/locale_next/locale_pl-PL.json @@ -1,15 +1,9 @@ { - "repo": { - "pulls": { - "merged_title_desc": { - "many": "scala %[1]d commity/ów z %[2]s do %[3]s %[4]s" - }, - "title_desc": { - "many": "chce scalić %[1]d commity/ów z %[2]s do %[3]s" - } - } - }, - "search": { - "milestone_kind": "Wyszukaj kamienie milowe..." - } + "repo.pulls.merged_title_desc": { + "many": "scala %[1]d commity/ów z %[2]s do %[3]s %[4]s" + }, + "repo.pulls.title_desc": { + "many": "chce scalić %[1]d commity/ów z %[2]s do %[3]s" + }, + "search.milestone_kind": "Wyszukaj kamienie milowe..." } diff --git a/options/locale_next/locale_pt-BR.json b/options/locale_next/locale_pt-BR.json index e7758ef1bf..4de44582a5 100644 --- a/options/locale_next/locale_pt-BR.json +++ b/options/locale_next/locale_pt-BR.json @@ -1,17 +1,11 @@ { - "repo": { - "pulls": { - "merged_title_desc": { - "one": "mesclou %[1]d commit de %[2]s em %[3]s %[4]s", - "other": "mesclou %[1]d commits de %[2]s em %[3]s %[4]s" - }, - "title_desc": { - "one": "quer mesclar %[1]d commit de %[2]s em %[3]s", - "other": "quer mesclar %[1]d commits de %[2]s em %[3]s" - } - } - }, - "search": { - "milestone_kind": "Pesquisar marcos..." - } + "repo.pulls.merged_title_desc": { + "one": "mesclou %[1]d commit de %[2]s em %[3]s %[4]s", + "other": "mesclou %[1]d commits de %[2]s em %[3]s %[4]s" + }, + "repo.pulls.title_desc": { + "one": "quer mesclar %[1]d commit de %[2]s em %[3]s", + "other": "quer mesclar %[1]d commits de %[2]s em %[3]s" + }, + "search.milestone_kind": "Pesquisar marcos..." } diff --git a/options/locale_next/locale_pt-PT.json b/options/locale_next/locale_pt-PT.json index 475023d461..cf72ef3e09 100644 --- a/options/locale_next/locale_pt-PT.json +++ b/options/locale_next/locale_pt-PT.json @@ -1,17 +1,11 @@ { - "repo": { - "pulls": { - "merged_title_desc": { - "one": "integrou %[1]d cometimento do ramo %[2]s no ramo %[3]s %[4]s", - "other": "integrou %[1]d cometimento(s) do ramo %[2]s no ramo %[3]s %[4]s" - }, - "title_desc": { - "one": "quer integrar %[1]d cometimento do ramo %[2]s no ramo %[3]s", - "other": "quer integrar %[1]d cometimento(s) do ramo %[2]s no ramo %[3]s" - } - } - }, - "search": { - "milestone_kind": "Procurar etapas..." - } + "repo.pulls.merged_title_desc": { + "one": "integrou %[1]d cometimento do ramo %[2]s no ramo %[3]s %[4]s", + "other": "integrou %[1]d cometimento(s) do ramo %[2]s no ramo %[3]s %[4]s" + }, + "repo.pulls.title_desc": { + "one": "quer integrar %[1]d cometimento do ramo %[2]s no ramo %[3]s", + "other": "quer integrar %[1]d cometimento(s) do ramo %[2]s no ramo %[3]s" + }, + "search.milestone_kind": "Procurar etapas..." } diff --git a/options/locale_next/locale_ru-RU.json b/options/locale_next/locale_ru-RU.json index de310505a3..36f54f405b 100644 --- a/options/locale_next/locale_ru-RU.json +++ b/options/locale_next/locale_ru-RU.json @@ -1,17 +1,11 @@ { - "repo": { - "pulls": { - "merged_title_desc": { - "one": "слит %[1]d коммит из %[2]s в %[3]s %[4]s", - "many": "слито %[1]d коммит(ов) из %[2]s в %[3]s %[4]s" - }, - "title_desc": { - "one": "хочет влить %[1]d коммит из %[2]s в %[3]s", - "many": "хочет влить %[1]d коммит(ов) из %[2]s в %[3]s" - } - } - }, - "search": { - "milestone_kind": "Найти этапы..." - } + "repo.pulls.merged_title_desc": { + "one": "слит %[1]d коммит из %[2]s в %[3]s %[4]s", + "many": "слито %[1]d коммит(ов) из %[2]s в %[3]s %[4]s" + }, + "repo.pulls.title_desc": { + "one": "хочет влить %[1]d коммит из %[2]s в %[3]s", + "many": "хочет влить %[1]d коммит(ов) из %[2]s в %[3]s" + }, + "search.milestone_kind": "Найти этапы..." } diff --git a/options/locale_next/locale_si-LK.json b/options/locale_next/locale_si-LK.json index 25e149f9b8..0cdd44acd0 100644 --- a/options/locale_next/locale_si-LK.json +++ b/options/locale_next/locale_si-LK.json @@ -1,12 +1,8 @@ { - "repo": { - "pulls": { - "merged_title_desc": { - "other": "මර්ජ්%[1]d සිට %[2]s දක්වා %[3]s %[4]s" - }, - "title_desc": { - "other": "%[1]d සිට %[2]s දක්වා %[3]s" - } - } - } + "repo.pulls.merged_title_desc": { + "other": "මර්ජ්%[1]d සිට %[2]s දක්වා %[3]s %[4]s" + }, + "repo.pulls.title_desc": { + "other": "%[1]d සිට %[2]s දක්වා %[3]s" + } } diff --git a/options/locale_next/locale_sv-SE.json b/options/locale_next/locale_sv-SE.json index fe68f161c2..de7099ad3a 100644 --- a/options/locale_next/locale_sv-SE.json +++ b/options/locale_next/locale_sv-SE.json @@ -1,15 +1,9 @@ { - "repo": { - "pulls": { - "merged_title_desc": { - "other": "sammanfogade %[1]d incheckningar från %[2]s in i %[3]s %[4]s" - }, - "title_desc": { - "other": "vill sammanfoga %[1]d incheckningar från s[2]s in i %[3]s" - } - } - }, - "search": { - "milestone_kind": "Sök milstolpar..." - } + "repo.pulls.merged_title_desc": { + "other": "sammanfogade %[1]d incheckningar från %[2]s in i %[3]s %[4]s" + }, + "repo.pulls.title_desc": { + "other": "vill sammanfoga %[1]d incheckningar från s[2]s in i %[3]s" + }, + "search.milestone_kind": "Sök milstolpar..." } diff --git a/options/locale_next/locale_tr-TR.json b/options/locale_next/locale_tr-TR.json index ef2cdb6584..f98b339245 100644 --- a/options/locale_next/locale_tr-TR.json +++ b/options/locale_next/locale_tr-TR.json @@ -1,15 +1,9 @@ { - "repo": { - "pulls": { - "merged_title_desc": { - "other": "%[4]s %[2]s içindeki %[1]d işlemeyi %[3]s ile birleştirdi" - }, - "title_desc": { - "other": "%[2]s içindeki %[1]d işlemeyi %[3]s ile birleştirmek istiyor" - } - } - }, - "search": { - "milestone_kind": "Kilometre taşlarını ara..." - } + "repo.pulls.merged_title_desc": { + "other": "%[4]s %[2]s içindeki %[1]d işlemeyi %[3]s ile birleştirdi" + }, + "repo.pulls.title_desc": { + "other": "%[2]s içindeki %[1]d işlemeyi %[3]s ile birleştirmek istiyor" + }, + "search.milestone_kind": "Kilometre taşlarını ara..." } diff --git a/options/locale_next/locale_uk-UA.json b/options/locale_next/locale_uk-UA.json index aded9786b4..ce37b9bef3 100644 --- a/options/locale_next/locale_uk-UA.json +++ b/options/locale_next/locale_uk-UA.json @@ -1,17 +1,11 @@ { - "repo": { - "pulls": { - "merged_title_desc": { - "one": "об'єднав %[1]d коміт з %[2]s в %[3]s %[4]s", - "many": "об'єднав %[1]d комітів з %[2]s в %[3]s %[4]s" - }, - "title_desc": { - "one": "хоче об'єднати %[1]d коміт з %[2]s в %[3]s", - "many": "хоче об'єднати %[1]d комітів з %[2]s в %[3]s" - } - } - }, - "search": { - "milestone_kind": "Шукати віхи..." - } + "repo.pulls.merged_title_desc": { + "one": "об'єднав %[1]d коміт з %[2]s в %[3]s %[4]s", + "many": "об'єднав %[1]d комітів з %[2]s в %[3]s %[4]s" + }, + "repo.pulls.title_desc": { + "one": "хоче об'єднати %[1]d коміт з %[2]s в %[3]s", + "many": "хоче об'єднати %[1]d комітів з %[2]s в %[3]s" + }, + "search.milestone_kind": "Шукати віхи..." } diff --git a/options/locale_next/locale_zh-CN.json b/options/locale_next/locale_zh-CN.json index 091b3fe609..10b1c9a4be 100644 --- a/options/locale_next/locale_zh-CN.json +++ b/options/locale_next/locale_zh-CN.json @@ -1,15 +1,9 @@ { - "repo": { - "pulls": { - "merged_title_desc": { - "other": "于 %[4]s 将 %[1]d 次代码提交从 %[2]s合并至 %[3]s" - }, - "title_desc": { - "other": "请求将 %[1]d 次代码提交从 %[2]s 合并至 %[3]s" - } - } - }, - "search": { - "milestone_kind": "搜索里程碑…" - } + "repo.pulls.merged_title_desc": { + "other": "于 %[4]s 将 %[1]d 次代码提交从 %[2]s合并至 %[3]s" + }, + "repo.pulls.title_desc": { + "other": "请求将 %[1]d 次代码提交从 %[2]s 合并至 %[3]s" + }, + "search.milestone_kind": "搜索里程碑…" } diff --git a/options/locale_next/locale_zh-HK.json b/options/locale_next/locale_zh-HK.json index dd7b954559..6baf89e022 100644 --- a/options/locale_next/locale_zh-HK.json +++ b/options/locale_next/locale_zh-HK.json @@ -1,9 +1,5 @@ { - "repo": { - "pulls": { - "merged_title_desc": { - "other": "於 %[4]s 將 %[1]d 次代碼提交從 %[2]s合併至 %[3]s" - } - } - } + "repo.pulls.merged_title_desc": { + "other": "於 %[4]s 將 %[1]d 次代碼提交從 %[2]s合併至 %[3]s" + } } diff --git a/options/locale_next/locale_zh-TW.json b/options/locale_next/locale_zh-TW.json index 4d31a713c0..d04e04c264 100644 --- a/options/locale_next/locale_zh-TW.json +++ b/options/locale_next/locale_zh-TW.json @@ -1,15 +1,9 @@ { - "repo": { - "pulls": { - "merged_title_desc": { - "other": "將 %[1]d 次提交從 %[2]s 合併至 %[3]s %[4]s" - }, - "title_desc": { - "other": "請求將 %[1]d 次程式碼提交從 %[2]s 合併至 %[3]s" - } - } - }, - "search": { - "milestone_kind": "搜尋里程碑..." - } + "repo.pulls.merged_title_desc": { + "other": "將 %[1]d 次提交從 %[2]s 合併至 %[3]s %[4]s" + }, + "repo.pulls.title_desc": { + "other": "請求將 %[1]d 次程式碼提交從 %[2]s 合併至 %[3]s" + }, + "search.milestone_kind": "搜尋里程碑..." } From f2e63495e7873f1f3d72a33a08071bd9ceea0042 Mon Sep 17 00:00:00 2001 From: Michael Kriese Date: Sat, 18 Jan 2025 15:19:53 +0100 Subject: [PATCH 19/23] chore(renovate): fix self-update config [skip-ci] --- renovate.json | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/renovate.json b/renovate.json index 64e610cd57..c34ae1aaba 100644 --- a/renovate.json +++ b/renovate.json @@ -110,34 +110,19 @@ "matchUpdateTypes": ["patch"], "automerge": true }, - { - "description": "Automerge renovate updates", - "matchDatasources": ["docker"], - "matchPackageNames": ["code.forgejo.org/forgejo-contrib/renovate"], - "matchUpdateTypes": ["minor", "patch", "digest"], - "automerge": true - }, { "description": "Add reviewer and additional labels to renovate PRs", "matchDatasources": ["docker"], - "matchPackageNames": ["code.forgejo.org/forgejo-contrib/renovate"], + "matchPackageNames": ["data.forgejo.org/renovate/renovate"], "reviewers": ["viceice"], "addLabels": ["forgejo/ci", "test/not-needed"] }, - { - "description": "Update renovate with higher prio to come through rate limit", - "matchDatasources": ["docker"], - "matchPackageNames": ["code.forgejo.org/forgejo-contrib/renovate"], - "extends": ["schedule:weekly"], - "prPriority": 10, - "groupName": "renovate" - }, { "description": "Disable renovate self-updates for release branches", "matchBaseBranches": ["/^v\\d+\\.\\d+\\/forgejo$/"], - "matchDatasources": ["docker"], "matchPackageNames": [ "code.forgejo.org/forgejo-contrib/renovate", + "data.forgejo.org/renovate/renovate", "ghcr.io/visualon/renovate" ], "enabled": false From e35afe475a9a1b5dba160d15dc30b7f2461ee34e Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Sat, 18 Jan 2025 14:56:11 +0000 Subject: [PATCH 20/23] Update renovate Docker tag to v39.115.4 (forgejo) (#6606) Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/6606 Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 039004603c..5865262d1a 100644 --- a/Makefile +++ b/Makefile @@ -49,7 +49,7 @@ GOVULNCHECK_PACKAGE ?= golang.org/x/vuln/cmd/govulncheck@v1 # renovate: datasour DEADCODE_PACKAGE ?= golang.org/x/tools/cmd/deadcode@v0.29.0 # renovate: datasource=go GOMOCK_PACKAGE ?= go.uber.org/mock/mockgen@v0.4.0 # renovate: datasource=go GOPLS_PACKAGE ?= golang.org/x/tools/gopls@v0.17.1 # renovate: datasource=go -RENOVATE_NPM_PACKAGE ?= renovate@39.111.0 # renovate: datasource=docker packageName=data.forgejo.org/renovate/renovate +RENOVATE_NPM_PACKAGE ?= renovate@39.115.4 # renovate: datasource=docker packageName=data.forgejo.org/renovate/renovate # https://github.com/disposable-email-domains/disposable-email-domains/commits/main/ DISPOSABLE_EMAILS_SHA ?= 0c27e671231d27cf66370034d7f6818037416989 # renovate: ... From d68e0d3e3903093019ce848d541efd60bf5fa1c1 Mon Sep 17 00:00:00 2001 From: Beowulf Date: Sat, 18 Jan 2025 19:39:42 +0000 Subject: [PATCH 21/23] fix(ui): hide git note add button for commit if commit already has a note (#6613) Regression from f5c0570533b0a835a88eb7337da841d071f2de6b Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/6613 Reviewed-by: 0ko <0ko@noreply.codeberg.org> Co-authored-by: Beowulf Co-committed-by: Beowulf --- templates/repo/commit_page.tmpl | 8 +++++--- tests/e2e/git-notes.test.e2e.ts | 3 +++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/templates/repo/commit_page.tmpl b/templates/repo/commit_page.tmpl index 66be0c143d..36de789dd1 100644 --- a/templates/repo/commit_page.tmpl +++ b/templates/repo/commit_page.tmpl @@ -128,9 +128,11 @@ -
- {{ctx.Locale.Tr "repo.diff.git-notes.add"}} -
+ {{if not .NoteRendered}} +
+ {{ctx.Locale.Tr "repo.diff.git-notes.add"}} +
+ {{end}} {{end}} diff --git a/tests/e2e/git-notes.test.e2e.ts b/tests/e2e/git-notes.test.e2e.ts index 4245853b24..1e2cbe76fc 100644 --- a/tests/e2e/git-notes.test.e2e.ts +++ b/tests/e2e/git-notes.test.e2e.ts @@ -8,6 +8,9 @@ test('Change git note', async ({page}) => { let response = await page.goto('/user2/repo1/commit/65f1bf27bc3bf70f64657658635e66094edbcb4d'); expect(response?.status()).toBe(200); + // An add button should not be present, because the commit already has a commit note + await expect(page.locator('#commit-notes-add-button')).toHaveCount(0); + await page.locator('#commit-notes-edit-button').click(); let textarea = page.locator('textarea[name="notes"]'); From 5f2d445d006a22c6adaabeb3e236a6c095785b0f Mon Sep 17 00:00:00 2001 From: Beowulf Date: Sun, 19 Jan 2025 01:42:00 +0100 Subject: [PATCH 22/23] fix(ui): add triangle down octicon to code search options dropdown This adds the triangle down oction to the code search options dropdown to match the other search option dropdowns (issue, pull). --- templates/shared/search/combo_multi.tmpl | 1 + 1 file changed, 1 insertion(+) diff --git a/templates/shared/search/combo_multi.tmpl b/templates/shared/search/combo_multi.tmpl index 89dc20b530..07d0ea8839 100644 --- a/templates/shared/search/combo_multi.tmpl +++ b/templates/shared/search/combo_multi.tmpl @@ -7,6 +7,7 @@
{{template "shared/search/input" dict "Value" .Value "Disabled" .Disabled "Placeholder" .Placeholder}}