Skip to content

Conversation

@KatherineInCode
Copy link
Contributor

🎟️ Tracking

https://bitwarden.atlassian.net/browse/PM-27098

📔 Objective

As noted in comments on a previous PR, the previous form of handling plurals in strings by breaking out just the plural had some issues. The first is that CrowdIn Translation Memory would cause issues with pluralized nouns being taken out of subject/object context, which doesn't work for languages that decline nouns differently for subjects and objects. The second is that languages with polypersonal agreement also require inflecting the verb congruently with the number of items, and we weren't allowing that.

Per that recommendation, I've pulled things out into full sentences, so that translators can provide better translations for the array of possibilities.

Nota bene: I elected even in circumstances where it is just a full sentence to leave the sentence-ending punctuation out of the pluralized forms. This allows us to remain consistent in multi-sentence situations, and for the base string to clearly indicate the sentence patterns. I'm not, however, wedded to that, I would just like to be consistent.

⏰ Reminders before review

  • Contributor guidelines followed
  • All formatters and local linters executed and passed
  • Written new unit and / or integration tests where applicable
  • Protected functional changes with optionality (feature flags)
  • Used internationalization (i18n) for all UI strings
  • CI builds passed
  • Communicated to DevOps any deployment requirements
  • Updated any necessary documentation (Confluence, contributing docs) or informed the documentation team

🦮 Reviewer guidelines

  • 👍 (:+1:) or similar for great changes
  • 📝 (:memo:) or ℹ️ (:information_source:) for notes or general info
  • ❓ (:question:) for questions
  • 🤔 (:thinking:) or 💭 (:thought_balloon:) for more open inquiry that's not quite a confirmed issue and could potentially benefit from discussion
  • 🎨 (:art:) for suggestions / improvements
  • ❌ (:x:) or ⚠️ (:warning:) for more significant problems or concerns needing attention
  • 🌱 (:seedling:) or ♻️ (:recycle:) for future improvements or indications of technical debt
  • ⛏ (:pick:) for minor or nitpick changes

@KatherineInCode KatherineInCode added the ai-review Request a Claude code review label Jan 9, 2026
@claude
Copy link

claude bot commented Jan 9, 2026

Claude finished @KatherineInCode's task in 2m 33s —— View job


Code Review Summary

Status: APPROVED - All localization changes are correctly implemented

Overview

This PR updates 4 plural string entries in Localizable.stringsdict from fragment-based to full-sentence forms to better support:

  • Languages with noun declensions (subject vs. object context)
  • Languages with polypersonal agreement (verb-noun number congruence)

Changes Analyzed

All 4 updated entries correctly preserve English output while enabling better translations:

  1. MasterPasswordMustBeAtLeastXCharactersLong - Validation message format updated
  2. ThisPasswordHasBeenExposedXTimesDescriptionLong - Data breach warning format updated
  3. XItemsSuccessfullyImported - Import success message format updated
  4. YourPINMustBeAtLeastXCharactersDescriptionLong - PIN requirement message format updated

Verification Performed

Correctness: Verified each entry produces identical English output

  • Master password validation: "Master password must be at least 8 characters long."
  • Password exposure: "This password has been exposed 1 time in data breaches. You should change it."
  • Import success: "1 item successfully imported"
  • PIN requirement: "Your PIN must be at least 4 characters. Your PIN settings will be reset..."

Consistency: Approach matches existing entry BitwardenCouldNotDecryptXVaultItemsDescriptionLong which already uses full-sentence pattern

Scope: Correctly targets only full-sentence entries; intentionally excludes simple noun plurals (XCharacters, XDays, XHours, etc.) used in menu selections

Format: XML structure follows Apple's stringsdict format correctly with proper key naming and structure

Assessment

Security: No security impact - localization only
Correctness: All changes verified correct
Breaking Changes: None - output identical for English
Performance: No impact
Maintainability: Improved - consistent pattern across all full-sentence plurals

No issues found. This is a well-scoped localization improvement that will benefit international users.

@github-actions
Copy link
Contributor

github-actions bot commented Jan 9, 2026

Logo
Checkmarx One – Scan Summary & Detailsa7d1a1fb-1101-43ea-bc5b-7639a8c86aff

Great job! No new security vulnerabilities introduced in this pull request

@codecov
Copy link

codecov bot commented Jan 9, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 84.27%. Comparing base (3818976) to head (a224fc2).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2240      +/-   ##
==========================================
- Coverage   85.57%   84.27%   -1.31%     
==========================================
  Files        1746     1989     +243     
  Lines      148371   163714   +15343     
==========================================
+ Hits       126974   137973   +10999     
- Misses      21397    25741    +4344     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@KatherineInCode KatherineInCode marked this pull request as ready for review January 9, 2026 17:35
@KatherineInCode KatherineInCode requested review from a team and matt-livefront as code owners January 9, 2026 17:35
@KatherineInCode
Copy link
Contributor Author

@mKoonrad I'd love to get your eyes on this, also, to make sure I'm not missing anything, and these updates to the plurals look reasonable to you.

Comment on lines +26 to +27
<string>%#@masterpasswordcharacters@.</string>
<key>masterpasswordcharacters</key>
Copy link
Member

Choose a reason for hiding this comment

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

🤔 Is it a requirement that this is fully in lowercase? If not, I feel like PascalCase is better/faster to read.
How do you feel about using the same as the main localization key here? in this case:

<string>%#@MasterPasswordMustBeAtLeastXCharactersLong@.</string>
<key>MasterPasswordMustBeAtLeastXCharactersLong</key>

Also, do you know if this <string> - <key> pair is presented in Crowdin somehow? Because if not, we could just simplify this to a generic way we can repeat in any localization:

<string>%#@pluralizedValue@.</string>
<key>pluralizedValue</key>

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The all-lowercase isn't required; that's just what I had done before. I'm fine if we want to move to camel case, though (which is, at least, what this guide to the format does). I'm hesitant to reuse a key in a substring, though, just to avoid any potential conflict.

I'm not inclined to do the generic way, as well, because of the potential issue with translation memory, which is one of the things we're trying to avoid by not putting the %#@items@ in the middle of a sentence, in addition to handling polypersonal agreement better.

It does show up in CrowdIn. Here's the vault items in Portuguese:
Screenshot 2026-01-12 at 3 42 09 PM

Also of interest here is that substrings end up as separate items to be translated—which makes sense, since my understanding is that you can nest substrings fairly deep if you really wanted to, and that provides a more-unified format for it, from a UX perspective. But that might want to inform how we structure things 🤔

Copy link
Member

Choose a reason for hiding this comment

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

Makes sense.
However, IMO we should have a convention to make substring keys to avoid unwanted repetition/conflicts. That's why I was saying to use the same convention we use to build the localization key to build the substring key, i.e. join the substring value in Pascal Case as it's the best way to guarantee we won't repeat the same substring key in other localizations that are different. This ends up being the original localization key when the whole value is the substring as well.
Moreover, it could be something else that has some sort of convention we could use based off the substring value as well if the parent localization key has conflicts but I strongly believe we should have some sort of convention for it to avoid potential problems and be faster to add new keys without checking the others substring keys.
Thoughts?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I agree that a convention would be helpful, though in a fair number of these, it's just the first sentence—which is what's already captured in the key for the whole phrase—that needs translated for number, so we might get substring keys that match the whole-phrase key 🤔 Though I suppose if we drop the X(/Y/Z/etc.) out of the key, that might accomplish what we want.

I'll swing through and update things CamelCase and see what I can do about establishing a more-obvious convention.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ai-review Request a Claude code review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants