Skip to content

Conversation

@caugner
Copy link
Contributor

@caugner caugner commented Jun 12, 2025

Description

(Ignore this PR.)

Motivation

Additional details

Related issues and pull requests

Copy link

@peiying2 peiying2 left a comment

Choose a reason for hiding this comment

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

In addition to the comments to the strings, here are a few more general comments:

  • Is there a staging URL to verify the translation? An example from mozilla.org project
  • en vs en-US locale - In case en-US has US specific content that's not applicable to other languages/countries, it's a better practice to use en as the source for localizable content
  • Speaking of locales, the Country code should be in capital letters, so en-US, pt-BR, zh-CN, zh-TW
  • Mozilla brand and product names: when referencing names, this approach ensures consistency. If there are more pages to add to the project, you can create a separate files for names only, and other pages can reference to it.
  • Links: I didn't call them all out but only commented in one string. They are typically coded like this `{ -brand-name-mozilla-corporation}.
  • The page lacks comments for context. Please add them even if it seems obvious in some cases. Please note the differences in comments for a section and comment for one string.

l10n/en-us.ftl Outdated
article-footer_last-modified = This page was last modified on <time data-l10n-name="date">{ $date }</timestamp> by <a data-l10n-name="contributors">MDN contributors</a>.
article-footer_source_title = Folder: { $folder } (Opens in a new tab)

baseline_asterisk = { $asterisk } Some parts of this feature may have varying levels of support.

Choose a reason for hiding this comment

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

What is this variable for? Or is it for formatting?

Choose a reason for hiding this comment

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

Looking into this it seems its just the character *. It's best to either use the character itself here, or if there area reasons for not wanting localizers to be able to adjust it, then perhaps its best to remove it from the string and style on the backend.

l10n/en-us.ftl Outdated
baseline_not_extra = This feature is not Baseline because it does not work in some of the most widely-used browsers.
baseline_supported_in = Supported in { $browsers }
baseline_unsupported_in = Not widely supported in { $browsers }
baseline_supported_and_unsupported_in = Supported in { $supported }, but not widely supported in { $unsupported }

Choose a reason for hiding this comment

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

What are shown in { $supported } and { $unsupported }? Add a comment will provide context will help.

l10n/en-us.ftl Outdated
footer_mofo = Visit <a data-l10n-name="moco">Mozilla Corporation’s</a> not-for-profit parent, the <a data-l10n-name="mofo">Mozilla Foundation</a>.
footer_copyright = Portions of this content are ©1998–2024 by individual mozilla.org contributors. Content available under <a data-l10n-name="cc">a Creative Commons license</a>.

search_title = Search results for: <em>{ $query }</em>

Choose a reason for hiding this comment

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

Need a comment on what the variable is for, like the kind of queries.

l10n/en-us.ftl Outdated
footer_copyright = Portions of this content are ©1998–2024 by individual mozilla.org contributors. Content available under <a data-l10n-name="cc">a Creative Commons license</a>.

search_title = Search results for: <em>{ $query }</em>
search_stats = Found { $results } matches in { $time } milliseconds.

Choose a reason for hiding this comment

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

So this string is about how fast results can be found, not how many results found in X number of articles.

l10n/en-us.ftl Outdated
Report = Report

obs_title = HTTP Observatory
obs_landing_intro = Launched in 2016, the HTTP Observatory enhances web security by analyzing compliance with best security practices. It has provided insights to over 6.9 million websites through 47 million scans.

Choose a reason for hiding this comment

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

Is it better to change the websites and scan numbers to variables to minimize the needs for updates in the future?

l10n/en-us.ftl Outdated
compat_legend_footnote = See implementation notes.
compat_legend_disabled = User must explicitly enable this feature.
compat_legend_altname = Uses a non-standard name.
compat_legend_prefix = Requires a vendor prefix or different name for use.

Choose a reason for hiding this comment

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

Need examples for vendor and prefix look like in this context.

l10n/en-us.ftl Outdated

baseline_asterisk = { $asterisk } Some parts of this feature may have varying levels of support.
baseline_high_extra = This feature is well established and works across many devices and browser versions. It’s been available across browsers since { $date }.
baseline_low_extra = Since { $date }, this feature works across the latest devices and browser versions. This feature might not work in older devices or browsers.

Choose a reason for hiding this comment

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

Nitpick, maybe changing the second This feature to It.

l10n/en-us.ftl Outdated
baseline_supported_and_unsupported_in = Supported in { $supported }, but not widely supported in { $unsupported }

homepage-hero_title = Resources for <u data-l10n-name="developers">Developers</u>,<br> by Developers
homepage-hero_description = Documenting <a data-l10n-name="css">CSS</a>, <a data-l10n-name="html">HTML</a>, and <a data-l10n-name="js">JavaScript</a>, since 2005.

Choose a reason for hiding this comment

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

Remove the comma before since 2005.

l10n/en-us.ftl Outdated
reference_toc_header = In this article

footer_mofo = Visit <a data-l10n-name="moco">Mozilla Corporation’s</a> not-for-profit parent, the <a data-l10n-name="mofo">Mozilla Foundation</a>.
footer_copyright = Portions of this content are ©1998–2024 by individual mozilla.org contributors. Content available under <a data-l10n-name="cc">a Creative Commons license</a>.

Choose a reason for hiding this comment

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

Can we make 1998–2024 dynamically updated?

l10n/en-us.ftl Outdated

reference_toc_header = In this article

footer_mofo = Visit <a data-l10n-name="moco">Mozilla Corporation’s</a> not-for-profit parent, the <a data-l10n-name="mofo">Mozilla Foundation</a>.

Choose a reason for hiding this comment

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

For links, we typically use this format <a href="{ $moco }">Mozilla Corporation</a> for example.

Choose a reason for hiding this comment

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

I think this depends on the backend, it appears that's how bedrock is doing things for their footer links, but might not be applicable in this case.

Copy link

@bcolsson bcolsson left a comment

Choose a reason for hiding this comment

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

In addition to the individual comments - we should consider if this needs separate brand terms for:
Mozilla
Mozilla Corporation
Mozilla Foundation
HTTP Observatory

This depends on how frequently new strings will be introduced once localization begings and how likely they'd include these terms.

l10n/en-us.ftl Outdated
@@ -0,0 +1,102 @@
article-footer_last-modified = This page was last modified on <time data-l10n-name="date">{ $date }</timestamp> by <a data-l10n-name="contributors">MDN contributors</a>.

Choose a reason for hiding this comment

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

Suggested change
article-footer_last-modified = This page was last modified on <time data-l10n-name="date">{ $date }</timestamp> by <a data-l10n-name="contributors">MDN contributors</a>.
article-footer_last-modified = This page was last modified on <timestamp data-l10n-name="date">{ $date }</timestamp> by <a data-l10n-name="contributors">MDN contributors</a>.

Tags don't match

l10n/en-us.ftl Outdated
article-footer_last-modified = This page was last modified on <time data-l10n-name="date">{ $date }</timestamp> by <a data-l10n-name="contributors">MDN contributors</a>.
article-footer_source_title = Folder: { $folder } (Opens in a new tab)

baseline_asterisk = { $asterisk } Some parts of this feature may have varying levels of support.

Choose a reason for hiding this comment

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

Looking into this it seems its just the character *. It's best to either use the character itself here, or if there area reasons for not wanting localizers to be able to adjust it, then perhaps its best to remove it from the string and style on the backend.

l10n/en-us.ftl Outdated
@@ -0,0 +1,102 @@
article-footer_last-modified = This page was last modified on <time data-l10n-name="date">{ $date }</timestamp> by <a data-l10n-name="contributors">MDN contributors</a>.

Choose a reason for hiding this comment

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

This is more stylistic - but can we consistently use dashes - instead of underscores for string IDs?
E.g. article-footer-last-modified

l10n/en-us.ftl Outdated
@@ -0,0 +1,102 @@
article-footer_last-modified = This page was last modified on <time data-l10n-name="date">{ $date }</timestamp> by <a data-l10n-name="contributors">MDN contributors</a>.
article-footer_source_title = Folder: { $folder } (Opens in a new tab)

Choose a reason for hiding this comment

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

Could you include a comment to describe variables? Something like the below:

Suggested change
article-footer_source_title = Folder: { $folder } (Opens in a new tab)
# Variables:
# $folder (String) - Link to github folder
article-footer_source_title = Folder: { $folder } (Opens in a new tab)

l10n/en-us.ftl Outdated
article-footer_source_title = Folder: { $folder } (Opens in a new tab)

baseline_asterisk = { $asterisk } Some parts of this feature may have varying levels of support.
baseline_high_extra = This feature is well established and works across many devices and browser versions. It’s been available across browsers since { $date }.

Choose a reason for hiding this comment

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

Could you include a comment for the variable here and similar date variables below?

Suggested change
baseline_high_extra = This feature is well established and works across many devices and browser versions. It’s been available across browsers since { $date }.
# Variables:
# $date (String) - a localized date string in format Month YYYY (e.g. December 2024)
baseline_high_extra = This feature is well established and works across many devices and browser versions. It’s been available across browsers since { $date }.

l10n/en-us.ftl Outdated

Report = Report

obs_title = HTTP Observatory

Choose a reason for hiding this comment

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

Should HTTP Observatory be localized or treated as a brand term / left unlocalized?

l10n/de.ftl Outdated
@@ -0,0 +1,33 @@
content-feedback_question = War diese Übersetzung hilfreich?

Choose a reason for hiding this comment

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

Noting here that there seem to be string IDs here that don't match anything in en-US source.

l10n/en-us.ftl Outdated
compat_deprecated = Deprecated
compat_experimental = Experimental
compat_nonstandard = Non-standard
compat_no = No

Choose a reason for hiding this comment

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

This likely needs a comment to say what "No" is refuting here (e.g. is this refusing some request, or is it short for No compatibility or something else?)

l10n/en-us.ftl Outdated
Comment on lines 86 to 90
sidebar-filter-matches = { $matches ->
[0] No matches
[1] { $matches } match
*[other] { $matches } matches
}

Choose a reason for hiding this comment

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

Suggested change
sidebar-filter-matches = { $matches ->
[0] No matches
[1] { $matches } match
*[other] { $matches } matches
}
sidebar-filter-matches = { $matches ->
[0] No matches
[one] { $matches } match
*[other] { $matches } matches
}

l10n/en-us.ftl Outdated
obs_assessment = Developed by Mozilla, the HTTP Observatory performs an in-depth assessment of a site’s HTTP headers and other key security configurations.
obs_scanning = Its automated scanning process provides developers and website administrators with detailed, actionable feedback, focusing on identifying and addressing potential security vulnerabilities.
obs_security = The tool is instrumental in helping developers and website administrators strengthen their sites against common security threats in a constantly advancing digital environment.
obs_mdn = The HTTP Observatory provides effective security insights, guided by Mozilla's expertise and commitment to a safer and more secure internet and based on well-established trends and guidelines.

Choose a reason for hiding this comment

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

Suggested change
obs_mdn = The HTTP Observatory provides effective security insights, guided by Mozilla's expertise and commitment to a safer and more secure internet and based on well-established trends and guidelines.
obs_mdn = The HTTP Observatory provides effective security insights, guided by Mozillas expertise and commitment to a safer and more secure internet and based on well-established trends and guidelines.

@caugner
Copy link
Contributor Author

caugner commented Jun 20, 2025

  • Is there a staging URL to verify the translation? An example from mozilla.org project

Currently, all PRs by privileged users lead to a review deployment, and we'll extend this to non-privileged users eventually.

  • en vs en-US locale - In case en-US has US specific content that's not applicable to other languages/countries, it's a better practice to use en as the source for localizable content

MDN is only served in en-US, not en, i.e. en redirects to en-US.

  • Speaking of locales, the Country code should be in capital letters, so en-US, pt-BR, zh-CN, zh-TW

Fixed in ea86ac4 (not on this branch).

  • Mozilla brand and product names: when referencing names, this approach ensures consistency. If there are more pages to add to the project, you can create a separate files for names only, and other pages can reference to it.

  • Links: I didn't call them all out but only commented in one string. They are typically coded like this `[{ -brand-name-mozilla-corporation}]({ $moco }).

  • The page lacks comments for context. Please add them even if it seems obvious in some cases. Please note the differences in comments for a section and comment for one string.

Thanks, I added action items for us, to migrate to kebab-case, use comments, and consider using terms.

@peiying2
Copy link

@caugner Any update on this?

@LeoMcA
Copy link
Member

LeoMcA commented Jul 21, 2025

Hey @peiying2 - sorry about the wait, the l10n work so far was just to ensure we had a functional API for development, rather than anything remotely usable by localisers. I've turned my attention to this now, but I need to explain a bit of our philosophy here:

I'm not a huge fan of writing strings out of context: I think it makes making mistakes easier, and is much harder for code contributors. Because of that, I've designed an API around inlining strings, like this (see the this.l10n template tags):

html`<div class="language_switcher__remember">
  <mdn-switch
    @toggle=${this._togglePreferredLocale}
    ?checked=${this._isLocalePreferred}
    >${this.l10n`Remember language`}</mdn-switch
  >
  <mdn-button
    variant="plain"
    .icon=${infoIcon}
    icon-only
    href="https://github.com/orgs/mdn/discussions/739"
    target="_blank"
    title=${this
      .l10n`Enable this setting to always switch to the current language when available. (Click to learn more.)`}
    >${this.l10n`Learn more`}</mdn-button
  >
</div>`

I've got a proof of concept string extractor which produces a Fluent file like the following:

remember-language-8ut5 = Remember language
enable-this-setting-to-always-sw-1ah2 = Enable this setting to always switch to the current language when available. (Click to learn more.)
learn-more-1o3l = Learn more

We do also support manually specifying the id, using syntax like this.l10n("default")`OS Default`. Otherwise for more complex strings - with arguments, etc. - we don't inline them (for now, at least).

I've got a bunch of questions about where to go from here:

  1. How much do IDs matter: you'll see I've made a slug out of the string, truncated it, and appended a hash (so e.g. "hello" and "Hello!" don't clash). Could I do away with the slug entirely and just use hashes? Or would it be best to not use hashes at all, and error in the case of clashes, telling the developer to manually specify the ID.

  2. Is there any issue in Pontoon with using a collection of many small Fluent files, rather than one big one with everything in? It could make life a lot easier for ID generation and overall performance if we have one Fluent file per component or even js file. How does Pontoon handle duplicated strings across files - if memory serves it has some kind of "this string is similar to this one already localised" functionality - so I hope it's not too bad for localisers.

  3. I'm aware that the same string in different contexts in one locale can require multiple strings in another. Are there sensible assumptions that can be taken here (e.g. the same English string used twice in the same component is probably localised the same), or is it a hard requirement that each string be unique in the Fluent file? How tight is the feedback loop from Pontoon: will it be easy for localisers to ping us and tell us that we need two separate IDs in this context?

I think that's it for now: I've noted your points about using comments and brands - I've got some ideas for how to implement that with the inline string approach - thank you!

@peiying2
Copy link

Hello @LeoMcA

I will have my colleague @bcolsson to answer the more technical questions. I will address the questions below for the time being

2. Is there any issue in Pontoon with using a collection of many small Fluent files, rather than one big one with everything in? It could make life a lot easier for ID generation and overall performance if we have one Fluent file per component or even js file. How does Pontoon handle duplicated strings across files - if memory serves it has some kind of "this string is similar to this one already localised" functionality - so I hope it's not too bad for localisers.

Most of the Fluent based web projects have multiple files, such as Relay, Mozilla.org and Firefox.com. They tend to provide better context through the file names. It's easier to manage than having one big file.

Having a smaller file is a lot easier for ID generation. Both firefox.com and mozilla.org use the file name as part of the string ID and the first few words in the string to make up the string ID. Here is an example.

Yes, Pontoon has a translation memory feature that shows localizers translations of identical or similar source content and rate them in % on the level of matches. Localizers can select the correct one based on context. Don't worry about duplicates.

3. I'm aware that the same string in different contexts in one locale can require multiple strings in another. Are there sensible assumptions that can be taken here (e.g. the same English string used twice in the same component is probably localised the same), or is it a hard requirement that each string be unique in the Fluent file? How tight is the feedback loop from Pontoon: will it be easy for localisers to ping us and tell us that we need two separate IDs in this context?

Preferably different ID for each of the strings even if they are the same. I remember a case where the same word is used for footer and navigation but in Turkish, they use different words for the same English words.

Add context in the comments. If there are variables, provide examples how the variable would show up. If localizers have questions or need clarification, there is a comment field associated with each string. They usually ask questions and the l10n team will relay the question.

You probably have seen these documents already. It's worth checking it out or share with the team if you haven't.

@peiying2
Copy link

@LeoMcA I want to share a document the Bedrock team put together on writing code in Fluent with L10n in mind.

@bcolsson
Copy link

How much do IDs matter: you'll see I've made a slug out of the string, truncated it, and appended a hash (so e.g. "hello" and "Hello!" don't clash). Could I do away with the slug entirely and just use hashes? Or would it be best to not use hashes at all, and error in the case of clashes, telling the developer to manually specify the ID.

Speaking in best practices: IDs matter quite a bit to both those reviewing the source strings (us) and those translating the strings. They often provide important context to understand things like whether something is a button, header, etc. In other projects, we often use name spacing to both keep IDs unique, while also making things easier to find - especially when trying to find which instance of a common string (e.g. "Password" or "Sign in") is referring to.

Other challenges I see:

  • Handling updates to strings. If a string needs to change we need to update the string ID to ensure that translations are re-triggered. (See here for details.) Using hashes would conceivably ensure a new string is created, but how do you know what the old string was for removal? There's also times when you want to make a slight change to a string and not update translations (adding a comma/period or changing capitalization), and a hash would make that impossible. It also makes it hard for string reviewers to determine whether strings are new or updated (and again, if the string ID isn't labeled in the code then it's hard to find where the string is used).
  • Touched on above: if a string is no longer used/removed in code, how would we know which strings are safe remove?
  • Generating Fluent strings by extracting them from code would also mean that by default we aren't adding comments to them.

My recommendation here would be to always manually specify IDs, name space them, but still have something check that there aren't duplicate IDs.

Is there any issue in Pontoon with using a collection of many small Fluent files, rather than one big one with everything in? It could make life a lot easier for ID generation and overall performance if we have one Fluent file per component or even js file. How does Pontoon handle duplicated strings across files - if memory serves it has some kind of "this string is similar to this one already localised" functionality - so I hope it's not too bad for localisers.

In addition to what Peiying mentioned about translation memories and name spacing, I'll note that we need to have unique string IDs regardless if they are in different files. (So you can't have a page-title in File A and also a page-title in File B, this is where name spacing comes in handy.)

I'm aware that the same string in different contexts in one locale can require multiple strings in another. Are there sensible assumptions that can be taken here (e.g. the same English string used twice in the same component is probably localised the same), or is it a hard requirement that each string be unique in the Fluent file? How tight is the feedback loop from Pontoon: will it be easy for localisers to ping us and tell us that we need two separate IDs in this context?

Best practices for localization strings is to prefer WET versus DRY code. These are hard errors to spot, especially if we aren't providing additional context via comments / string IDs.

@peiying2
Copy link

@LeoMcA let us know if anything is unclear in our comments. We can also arrange a call to go over some of the issues if it's easier.

@LeoMcA
Copy link
Member

LeoMcA commented Aug 26, 2025

Apologies, I was wrapped up getting fred ready for launch.

All great advice: thank you. #452 is a first step, making every string technically localisable, which we can build upon following your advice to improve the l10n experience.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants