Skip to content

Conversation

@lunny
Copy link
Member

@lunny lunny commented Dec 21, 2025

Crowdin does not remove empty lines in nested JSON translation files. Therefore, we use flattened translation keys instead. We have also updated the key-loading logic to ensure that empty values are not applied during translation.

@lunny lunny added the type/bug label Dec 21, 2025
@GiteaBot GiteaBot added the lgtm/need 2 This PR needs two approvals by maintainers to be considered for merging. label Dec 21, 2025
@github-actions github-actions bot added modifies/go Pull requests that update Go code modifies/internal labels Dec 21, 2025
@silverwind
Copy link
Member

silverwind commented Dec 21, 2025

Or just flattify them as I had suggested? Would that avoid the problem?

Copy link
Contributor

@TheFox0x7 TheFox0x7 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure what's the best approach here (flat/nested) and I don't know much about i18n so this is purely from code side.

@lunny
Copy link
Member Author

lunny commented Dec 21, 2025

Or just flattify them as I had suggested? Would that avoid the problem?

Based on Crowdin’s issue responses, yes—using a flat JSON structure would follow the skip_untranslated_strings: true configuration.

However, I’m not sure whether we should convert to a flat structure at this stage. It may be better to revisit this after the current change is merged and, if needed, propose a separate PR to handle the conversion.

@wxiaoguang
Copy link
Contributor

wxiaoguang commented Dec 21, 2025

However, I’m not sure whether we should convert to a flat structure at this stage. It may be better to revisit this after the current change is merged and, if needed, propose a separate PR to handle the conversion.

It is sure it should covert to a flat layout since #35489 (comment), when you first found that the translations were broken, it is a good chance to do all-in-one fix.

Since the translations are being hugely changed in recent days, I don't think it makes sense to do so again in the future.

You need a complete plan for this task, and ideally you should test the solution in a separate project & repo before applying it into Gitea's main repo.

@lunny
Copy link
Member Author

lunny commented Dec 22, 2025

However, I’m not sure whether we should convert to a flat structure at this stage. It may be better to revisit this after the current change is merged and, if needed, propose a separate PR to handle the conversion.

It is sure it should covert to a flat layout since #35489 (comment), when you first found that the translations were broken, it is a good chance to do all-in-one fix.

Since the translations are being hugely changed in recent days, I don't think it makes sense to do so again in the future.

You need a complete plan for this task, and ideally you should test the solution in a separate project & repo before applying it into Gitea's main repo.

Using a flat structure could be another PR, this PR will focus on fixing the current partial broken translations.

@lunny lunny added the skip-changelog This PR is irrelevant for the (next) changelog, for example bug fixes for unreleased features. label Dec 22, 2025
@GiteaBot GiteaBot added lgtm/need 1 This PR needs approval from one additional maintainer to be merged. and removed lgtm/need 2 This PR needs two approvals by maintainers to be considered for merging. labels Dec 22, 2025
@silverwind
Copy link
Member

silverwind commented Dec 23, 2025

Iterating over JSON as raw text feels wrong. I feel like this could also be done in a Node script that only works on the parsed content. In Javascript, map/object iteration is based on insertation order so parsing and stringifying JSON are strictly inverse operations to each other (unlike on golang where map iteration order is random).

@silverwind
Copy link
Member

BTW why do we remove empty translations in first place? Couldn't we just make the translation code ignore empty translations and fall back to the default locale?

@wxiaoguang
Copy link
Contributor

I also don't understand why so many fragile dirty patches, but not have a complete solution : #36225 (comment)

@lunny lunny changed the title Fix update locales because crowdin will not remove empty translated value if it's a nested json format Use flatten translation keys Dec 24, 2025
@lunny
Copy link
Member Author

lunny commented Dec 24, 2025

This is ready view again.

lunny and others added 2 commits December 24, 2025 15:55
Co-authored-by: silverwind <[email protected]>
Signed-off-by: Lunny Xiao <[email protected]>
Co-authored-by: silverwind <[email protected]>
Signed-off-by: Lunny Xiao <[email protected]>
Comment on lines +134 to +135
format = l.idxToMsgMap[idx]
if format == "" { // missing translation in this locale, fallback to default
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not keep the old logic? I believe old logic is stricter and better.

If the old logic didn't catch the empty strings, you won't even realize them.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there should be a tool to detect untranslated strings, but currently there isn’t one. (I will continue #34737 after this merged)

With the new logic, empty translations will simply be ignored. I also couldn’t find any mechanism in the old logic that helped surface these missing translations. Without such tooling, it’s impractical to manually discover empty translations, since you can’t reasonably check every page without knowing where the issue occurs.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't understand, it's up to you, you decide.

@tyroneyeh
Copy link
Contributor

Hi Gitea team,

With the recent move from INI to JSON for translations, I’d like to suggest considering a further step: adopting a gettext-like i18n model where the English source string itself is used as the translation key.

For example:

with JSON like:

ctx.Tr("Sign In")

{
    "Sign In": "登录",
}

instead of symbolic keys such as:

ctx.Tr("sign_in")

{
    "sign_in": "登录",
}

The idea is to treat the English UI text as the single source of truth.

Benefits:

  1. Readability and developer experience
    Using source strings directly makes code and templates much easier to read and review.
    Developers immediately see what will be rendered in the UI without having to look up keys.
    This lowers the barrier for new contributors and reduces cognitive overhead in reviews.

  2. Less abstraction and maintenance overhead
    Symbolic keys introduce an extra layer that must be invented, documented, and kept in sync with the actual UI text.
    With source-string keys, the text itself is the identifier, which removes the need to design and maintain a separate key namespace.

  3. Alignment with gettext ecosystem
    This model is widely used in gettext-based systems and many large projects.
    There is a mature ecosystem of tools for extracting strings, merging updates, and handling fuzzy matches when source text changes.

  4. Strong fit with Crowdin’s workflow
    Crowdin’s translation UI is already source-text centric: translators work directly with the English source string and provide the target translation.
    Treating the source string as the key maps naturally to Crowdin’s translation units and avoids an extra indirection through symbolic keys.
    Crowdin also supports:

    • context display for translators,
    • deduplication of identical source strings,
    • and placeholder handling,
      which fits well with a gettext-like workflow.
  5. Better experience for translators
    Translators see the real UI text as the primary identifier, rather than opaque keys like "repo.create" or "dashboard".
    This improves clarity and reduces the need for external documentation to explain what each key represents.

Maintainability considerations:

  • Although changing a source string also changes the key, this is a well-known aspect of gettext-style systems and is addressed by existing tooling (fuzzy matching, merge tools, Crowdin’s similarity matching).
  • For strings that need different translations in different contexts (e.g. "Open"), a context mechanism similar to gettext’s msgctxt could be introduced if needed.
  • Overall, having a single, human-readable source of truth for UI text simplifies long-term maintenance compared to managing a parallel symbolic key space.

In summary, moving towards a source-string based i18n model could:

  • improve code readability and contributor experience,
  • reduce maintenance overhead around translation keys,
  • and integrate more naturally with Crowdin and existing translation tooling.

I believe this direction would make Gitea’s i18n system cleaner and more approachable in the long run.

Thanks for your consideration.

"startpage.lightweight": "輕量級",
"startpage.lightweight_desc": "一片便宜的 Raspberry Pi 就可以滿足 Gitea 的最低需求。節省您的機器資源!",
"startpage.license": "開放原始碼",
"startpage.license_desc": "取得 <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://code.gitea.io/gitea\">code.gitea.io/gitea</a> !成為一名<a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/go-gitea/gitea\">貢獻者</a>和我們一起讓 Gitea 更好,快點加入我們吧!",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a problem with including the URL directly in the translation.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There should not be any HTML in translation, only text. There are ways with printf to avoid this but so far no one has gotten around to it.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There should be a next step to replace all these links but not in this PR.

@GiteaBot GiteaBot added lgtm/done This PR has enough approvals to get merged. There are no important open reservations anymore. and removed lgtm/need 1 This PR needs approval from one additional maintainer to be merged. labels Dec 25, 2025
@silverwind
Copy link
Member

symbolic keys

I think it's okay to consider. Only downside that I see is the inability to "provide a hint" to the translator. Sometimes the same string can have different meanings based on context where it is used, and that context can be provided as part of the translation key.

@lunny
Copy link
Member Author

lunny commented Dec 25, 2025

symbolic keys

I think it's okay to consider. Only downside that I see is the inability to "provide a hint" to the translator. Sometimes the same string can have different meanings based on context where it is used, and that context can be provided as part of the translation key.

Yes, I think we need to have more time to consider this solution.

@lunny lunny added the reviewed/wait-merge This pull request is part of the merge queue. It will be merged soon. label Dec 25, 2025
@lunny lunny merged commit 324dcf6 into go-gitea:main Dec 25, 2025
24 checks passed
@GiteaBot GiteaBot added this to the 1.26.0 milestone Dec 25, 2025
@lunny lunny deleted the lunny/update_locales branch December 25, 2025 20:51
@GiteaBot GiteaBot removed the reviewed/wait-merge This pull request is part of the merge queue. It will be merged soon. label Dec 25, 2025
zjjhot added a commit to zjjhot/gitea that referenced this pull request Dec 26, 2025
* giteaofficial/main:
  Fix panic when get editor config file (go-gitea#36241)
  Refactor compare router param parse (go-gitea#36105)
  [skip ci] Updated translations via Crowdin
  Use flatten translation keys (go-gitea#36225)
  Replace CSRF cookie with `CrossOriginProtection` (go-gitea#36183)
  Remove fomantic form module (go-gitea#36222)
  Fix panic in blame view when a file has only a single commit (go-gitea#36230)
  fix: spelling error in migrate-storage cmd utility (go-gitea#36226)

# Conflicts:
#	templates/user/settings/security/twofa.tmpl
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

lgtm/done This PR has enough approvals to get merged. There are no important open reservations anymore. modifies/go Pull requests that update Go code modifies/internal skip-changelog This PR is irrelevant for the (next) changelog, for example bug fixes for unreleased features. type/bug

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants