Bug 1555788 - Migrate about preferences home content to use fluent r=fluent-reviewers,Mardak,pdahiya,flod

Differential Revision: https://phabricator.services.mozilla.com/D35278

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Jeane Carlos 2019-07-02 17:25:55 +00:00
parent 54909f9858
commit fbe42f7796
7 changed files with 246 additions and 112 deletions

View File

@ -5,7 +5,6 @@
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
const {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.defineModuleGetter(this, "PluralForm", "resource://gre/modules/PluralForm.jsm");
const {actionTypes: at} = ChromeUtils.import("resource://activity-stream/common/Actions.jsm");
XPCOMUtils.defineLazyGlobalGetters(this, ["fetch"]);
@ -19,7 +18,7 @@ const PREFS_BEFORE_SECTIONS = [
id: "search",
pref: {
feed: "showSearch",
titleString: "prefs_search_header",
titleString: "home-prefs-search-header",
},
icon: "chrome://browser/skin/search-glass.svg",
},
@ -27,8 +26,8 @@ const PREFS_BEFORE_SECTIONS = [
id: "topsites",
pref: {
feed: "feeds.topsites",
titleString: "settings_pane_topsites_header",
descString: "prefs_topsites_description",
titleString: "home-prefs-topsites-header",
descString: "home-prefs-topsites-description",
},
icon: "topsites",
maxRows: 4,
@ -41,8 +40,8 @@ const PREFS_AFTER_SECTIONS = [
id: "snippets",
pref: {
feed: "feeds.snippets",
titleString: "settings_pane_snippets_header",
descString: "prefs_snippets_description",
titleString: "home-prefs-snippets-header",
descString: "home-prefs-snippets-description",
},
icon: "info",
},
@ -149,20 +148,11 @@ this.AboutPreferences = class AboutPreferences {
const createAppend = (tag, parent, options) => parent.appendChild(
document.createXULElement(tag, options));
// Helper to get strings and format with values if necessary
const formatString = id => {
if (typeof id !== "object") {
return strings[id] || id;
}
let string = strings[id.id] || JSON.stringify(id);
if (id.values) {
Object.entries(id.values).forEach(([key, val]) => {
string = string.replace(new RegExp(`{${key}}`, "g"), val);
});
}
return string;
};
// Helper to get fluentIDs sometimes encase in an object
const getString = message =>
typeof message !== "object" ? message : message.id;
// Helper to link a UI element to a preference for updating
const linkPref = (element, name, type) => {
const fullPref = `browser.newtabpage.activity-stream.${name}`;
@ -183,11 +173,12 @@ this.AboutPreferences = class AboutPreferences {
const contentsGroup = homeGroup.insertAdjacentElement("afterend", homeGroup.cloneNode());
contentsGroup.id = "homeContentsGroup";
contentsGroup.setAttribute("data-subcategory", "contents");
createAppend("label", contentsGroup)
.appendChild(document.createElementNS(HTML_NS, "h2"))
.textContent = formatString("prefs_home_header");
createAppend("description", contentsGroup)
.textContent = formatString("prefs_home_description");
const homeHeader = createAppend("label", contentsGroup)
.appendChild(document.createElementNS(HTML_NS, "h2"));
document.l10n.setAttributes(homeHeader, "home-prefs-content-header");
const homeDescription = createAppend("description", contentsGroup);
document.l10n.setAttributes(homeDescription, "home-prefs-content-description");
// Add preferences for each section
prefStructure.forEach(sectionData => {
@ -201,7 +192,7 @@ this.AboutPreferences = class AboutPreferences {
} = sectionData;
const {
feed: name,
titleString,
titleString = {},
descString,
nestedPrefs = [],
} = prefData || {};
@ -220,8 +211,9 @@ this.AboutPreferences = class AboutPreferences {
sectionVbox.setAttribute("data-subcategory", id);
const checkbox = createAppend("checkbox", sectionVbox);
checkbox.classList.add("section-checkbox");
checkbox.setAttribute("label", formatString(titleString));
checkbox.setAttribute("src", iconUrl);
document.l10n.setAttributes(checkbox, getString(titleString), titleString.values);
linkPref(checkbox, name, "bool");
// Specially add a link for stories
@ -233,8 +225,8 @@ this.AboutPreferences = class AboutPreferences {
const link = createAppend("label", sponsoredHbox, {is: "text-link"});
link.classList.add("learn-sponsored");
link.setAttribute("href", sectionData.learnMore.link.href);
link.textContent = formatString(sectionData.learnMore.link.id);
link.setAttribute("href", sectionData.pref.learnMore.link.href);
document.l10n.setAttributes(link, sectionData.pref.learnMore.link.id);
}
// Add more details for the section (e.g., description, more prefs)
@ -243,7 +235,7 @@ this.AboutPreferences = class AboutPreferences {
if (descString) {
const label = createAppend("label", detailVbox);
label.classList.add("indent");
label.textContent = formatString(descString);
document.l10n.setAttributes(label, getString(descString));
// Add a rows dropdown if we have a pref to control and a maximum
if (rowsPref && maxRows) {
@ -260,9 +252,8 @@ this.AboutPreferences = class AboutPreferences {
menulist.setAttribute("crop", "none");
const menupopup = createAppend("menupopup", menulist);
for (let num = 1; num <= maxRows; num++) {
const plurals = formatString({id: "prefs_section_rows_option", values: {num}});
const item = createAppend("menuitem", menupopup);
item.setAttribute("label", PluralForm.get(num, plurals));
document.l10n.setAttributes(item, "home-prefs-sections-rows-option", { num });
item.setAttribute("value", num);
}
linkPref(menulist, rowsPref, "int");
@ -277,7 +268,7 @@ this.AboutPreferences = class AboutPreferences {
nestedPrefs.forEach(nested => {
const subcheck = createAppend("checkbox", detailVbox);
subcheck.classList.add("indent");
subcheck.setAttribute("label", formatString(nested.titleString));
document.l10n.setAttributes(subcheck, nested.titleString);
linkPref(subcheck, nested.name, "bool");
subChecks.push(subcheck);
subcheck.disabled = !pref._value;

View File

@ -19,13 +19,19 @@ const BUILT_IN_SECTIONS = {
"feeds.section.topstories": options => ({
id: "topstories",
pref: {
titleString: {id: "header_recommended_by", values: {provider: options.provider_name}},
descString: {id: "prefs_topstories_description2"},
titleString: {id: "home-prefs-recommended-by-header", values: {provider: options.provider_name} },
descString: {id: "home-prefs-recommended-by-description"},
nestedPrefs: options.show_spocs ? [{
name: "showSponsored",
titleString: "prefs_topstories_options_sponsored_label",
titleString: "home-prefs-recommended-by-option-sponsored-stories",
icon: "icon-info",
}] : [],
learnMore: {
link: {
href: "https://getpocket.com/firefox/new_tab_learn_more",
id: "home-prefs-recommended-by-learn-more",
},
},
},
shouldHidePref: options.hidden,
eventSource: "TOP_STORIES",
@ -35,7 +41,6 @@ const BUILT_IN_SECTIONS = {
link: {
href: "https://getpocket.com/firefox/new_tab_learn_more",
message: {id: "newtab-pocket-how-it-works"},
id: "pocket_how_it_works",
},
},
privacyNoticeURL: "https://www.mozilla.org/privacy/firefox/#suggest-relevant-content",
@ -53,20 +58,20 @@ const BUILT_IN_SECTIONS = {
"feeds.section.highlights": options => ({
id: "highlights",
pref: {
titleString: {id: "settings_pane_highlights_header"},
descString: {id: "prefs_highlights_description"},
titleString: {id: "home-prefs-highlights-header"},
descString: {id: "home-prefs-highlights-description"},
nestedPrefs: [{
name: "section.highlights.includeVisited",
titleString: "prefs_highlights_options_visited_label",
titleString: "home-prefs-highlights-option-visited-pages",
}, {
name: "section.highlights.includeBookmarks",
titleString: "settings_pane_highlights_options_bookmarks",
titleString: "home-prefs-highlights-options-bookmarks",
}, {
name: "section.highlights.includeDownloads",
titleString: "prefs_highlights_options_download_label",
titleString: "home-prefs-highlights-option-most-recent-download",
}, {
name: "section.highlights.includePocket",
titleString: "prefs_highlights_options_pocket_label",
titleString: "home-prefs-highlights-option-saved-to-pocket",
}],
},
shouldHidePref: false,

View File

@ -1,42 +1,3 @@
# LOCALIZATION NOTE(header_recommended_by): This is followed by the name
# of the corresponding content provider.
header_recommended_by=Recommended by {provider}
# LOCALIZATION NOTE (prefs_*, settings_*): These are shown in about:preferences
# for a "Firefox Home" section. "Firefox" should be treated as a brand and kept
# in English, while "Home" should be localized matching the about:preferences
# sidebar mozilla-central string for the panel that has preferences related to
# what is shown for the homepage, new windows, and new tabs.
prefs_home_header=Firefox Home Content
prefs_home_description=Choose what content you want on your Firefox Home screen.
prefs_content_discovery_description=Content Discovery in Firefox Home allows you to discover high-quality, relevant articles from across the web.
# LOCALIZATION NOTE (prefs_section_rows_option): This is a semi-colon list of
# plural forms used in a drop down of multiple row options (1 row, 2 rows).
# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
prefs_section_rows_option={num} row;{num} rows
prefs_search_header=Web Search
prefs_topsites_description=The sites you visit most
prefs_topstories_description2=Great content from around the web, personalized for you
prefs_topstories_options_sponsored_label=Sponsored Stories
prefs_topstories_sponsored_learn_more=Learn more
prefs_highlights_description=A selection of sites that youve saved or visited
prefs_highlights_options_visited_label=Visited Pages
prefs_highlights_options_download_label=Most Recent Download
prefs_highlights_options_pocket_label=Pages Saved to Pocket
prefs_snippets_description=Updates from Mozilla and Firefox
settings_pane_topsites_header=Top Sites
settings_pane_highlights_header=Highlights
settings_pane_highlights_options_bookmarks=Bookmarks
# LOCALIZATION NOTE(settings_pane_snippets_header): For the "Snippets" feature
# traditionally on about:home. Alternative translation options: "Small Note" or
# something that expresses the idea of "a small message, shortened from
# something else, and non-essential but also not entirely trivial and useless."
settings_pane_snippets_header=Snippets
pocket_how_it_works=How it works
# LOCALIZATION NOTE (firstrun_*). These strings are displayed only once, on the
# firstrun of the browser, they give an introduction to Firefox and Sync.
firstrun_title=Take Firefox with You

View File

@ -153,6 +153,12 @@ describe("AboutPreferences Feed", () => {
const testRender = () => instance.renderPreferences({
document: {
createXULElement: sandbox.stub().returns(node),
l10n: {
setAttributes(el, id , args) {
el.setAttribute("data-l10n-id", id);
el.setAttribute("data-l10n-args", JSON.stringify(args));
},
},
createProcessingInstruction: sandbox.stub(),
createElementNS: sandbox.stub().callsFake((NS, el) => node),
getElementById: sandbox.stub().returns(node),
@ -183,42 +189,27 @@ describe("AboutPreferences Feed", () => {
};
gHomePane = {toggleRestoreDefaultsBtn: sandbox.stub()};
});
describe("#formatString", () => {
it("should fall back to string id if missing", () => {
testRender();
assert.equal(node.textContent, "prefs_home_description");
});
it("should use provided plain string", () => {
strings = {prefs_home_description: "hello"};
describe("#getString", () => {
it("should not fail if titleString is not provided", () => {
prefStructure = [{pref: {}}];
testRender();
assert.equal(node.textContent, "hello");
assert.calledWith(node.setAttribute, "data-l10n-id", sinon.match.typeOf("undefined"));
});
it("should fall back to string object if missing", () => {
const titleString = {id: "foo"};
it("should return the string id if titleString is just a string", () => {
const titleString = "foo";
prefStructure = [{pref: {titleString}}];
testRender();
assert.calledWith(node.setAttribute, "label", JSON.stringify(titleString));
assert.calledWith(node.setAttribute, "data-l10n-id", titleString);
});
it("should use provided string object id", () => {
strings = {foo: "bar"};
prefStructure = [{pref: {titleString: {id: "foo"}}}];
it("should set id and args if titleString is an object with id and values", () => {
const titleString = {id: "foo", values: { provider: "bar"}};
prefStructure = [{pref: {titleString}}];
testRender();
assert.calledWith(node.setAttribute, "label", "bar");
});
it("should use values in string object", () => {
strings = {foo: "l{n}{n}t"};
prefStructure = [{pref: {titleString: {id: "foo", values: {n: 3}}}}];
testRender();
assert.calledWith(node.setAttribute, "label", "l33t");
assert.calledWith(node.setAttribute, "data-l10n-id", titleString.id);
assert.calledWith(node.setAttribute, "data-l10n-args", JSON.stringify(titleString.values));
});
});
describe("#linkPref", () => {
@ -268,11 +259,11 @@ describe("AboutPreferences Feed", () => {
testRender();
assert.calledWith(node.setAttribute, "label", titleString);
assert.calledWith(node.setAttribute, "data-l10n-id", titleString);
});
it("should add a link for top stories", () => {
const href = "https://disclaimer/";
prefStructure = [{learnMore: {link: {href}}, id: "topstories", pref: {feed: "feed"}}];
prefStructure = [{id: "topstories", pref: {feed: "feed", learnMore: {link: {href}}}}];
testRender();
assert.calledWith(node.setAttribute, "href", href);
@ -285,7 +276,7 @@ describe("AboutPreferences Feed", () => {
testRender();
assert.equal(node.textContent, descString);
assert.calledWith(node.setAttribute, "data-l10n-id", descString);
});
it("should render rows dropdown with appropriate number", () => {
prefStructure = [{rowsPref: "row_pref", maxRows: 3, pref: {descString: "foo"}}];
@ -305,7 +296,7 @@ describe("AboutPreferences Feed", () => {
it("should render a nested pref", () => {
testRender();
assert.calledWith(node.setAttribute, "label", titleString);
assert.calledWith(node.setAttribute, "data-l10n-id", titleString);
});
it("should add a change event", () => {
testRender();

View File

@ -26,6 +26,7 @@
<linkset>
<html:link rel="localization" href="branding/brand.ftl"/>
<html:link rel="localization" href="browser/branding/brandings.ftl"/>
<html:link rel="localization" href="browser/branding/sync-brand.ftl"/>
<html:link rel="localization" href="browser/preferences/preferences.ftl"/>
<!-- Used by fontbuilder.js -->

View File

@ -498,6 +498,53 @@ choose-bookmark =
.label = Use Bookmark…
.accesskey = B
## Home Section - Firefox Home Content Customization
home-prefs-content-header = Firefox Home Content
home-prefs-content-description = Choose what content you want on your Firefox Home screen.
home-prefs-content-discovery-description = Content Discovery in Firefox Home allows you to discover high-quality, relevant articles from across the web.
home-prefs-search-header =
.label = Web Search
home-prefs-topsites-header =
.label = Top Sites
home-prefs-topsites-description = The sites you visit most
# Variables:
# $provider (String): Name of the corresponding content provider, e.g "Pocket".
home-prefs-recommended-by-header =
.label = Recommended by { $provider }
home-prefs-recommended-by-description = Great content from around the web, personalized for you
home-prefs-recommended-by-learn-more = How it works
home-prefs-recommended-by-option-sponsored-stories =
.label = Sponsored Stories
home-prefs-highlights-header =
.label = Highlights
home-prefs-highlights-description = A selection of sites that youve saved or visited
home-prefs-highlights-option-visited-pages =
.label = Visited Pages
home-prefs-highlights-options-bookmarks =
.label = Bookmarks
home-prefs-highlights-option-most-recent-download =
.label = Most Recent Download
home-prefs-highlights-option-saved-to-pocket =
.label = Pages Saved to { -pocket-brand-name }
# For the "Snippets" feature traditionally on about:home.
# Alternative translation options: "Small Note" or something that
# expresses the idea of "a small message, shortened from something else,
# and non-essential but also not entirely trivial and useless.
home-prefs-snippets-header =
.label = Snippets
home-prefs-snippets-description = Updates from { -vendor-short-name } and { -brand-product-name }
home-prefs-sections-rows-option =
.label =
{ $num ->
[one] { $num } row
*[other] { $num } rows
}
## Search Section
search-bar-header = Search Bar

View File

@ -0,0 +1,138 @@
# coding=utf8
# Any copyright is dedicated to the Public Domain.
# http://creativecommons.org/publicdomain/zero/1.0/
from __future__ import absolute_import
import fluent.syntax.ast as FTL
from fluent.migrate.helpers import transforms_from
from fluent.migrate.helpers import TERM_REFERENCE, VARIABLE_REFERENCE
from fluent.migrate import REPLACE, REPLACE_IN_TEXT, PLURALS
TARGET_FILE = 'browser/browser/preferences/preferences.ftl'
SOURCE_FILE = 'browser/browser/preferences/preferences.ftl'
"""
From mozilla-central directory:
```
cd ..
git clone hg::https://hg.mozilla.org/l10n/fluent-migration
cd fluent-migration
pip install -e .
cd ..
hg clone https://hg.mozilla.org/l10n/gecko-strings
```
NB: gecko-strings needs to be cloned with mercurial not git-cinnabar
Testing from mozilla-central directory:
```
PYTHONPATH=./python/l10n/fluent_migrations migrate-l10n bug_1555788_newtab --lang en-US --reference-dir . \
--localization-dir ../gecko-strings
diff -B -w browser/locales/en-US/browser/preferences/preferences.ftl ../gecko-strings/browser/browser/preferences/preferences.ftl
```
"""
def migrate(ctx):
"""Bug 1555788 - Migrate about preferences home content to use fluent, part {index}"""
ctx.add_transforms(
TARGET_FILE,
SOURCE_FILE,
transforms_from(
"""
home-prefs-content-header = { COPY(from_path, "prefs_home_header") }
home-prefs-content-description = { COPY(from_path, "prefs_home_description") }
home-prefs-content-discovery-description = { COPY(from_path, "prefs_content_discovery_description") }
home-prefs-search-header =
.label = { COPY(from_path, "prefs_search_header") }
home-prefs-highlights-header =
.label = { COPY(from_path, "settings_pane_highlights_header") }
home-prefs-highlights-description = { COPY(from_path, "prefs_highlights_description") }
home-prefs-highlights-option-visited-pages =
.label = { COPY(from_path, "prefs_highlights_options_visited_label") }
home-prefs-highlights-option-most-recent-download =
.label = { COPY(from_path, "prefs_highlights_options_download_label") }
home-prefs-highlights-options-bookmarks =
.label = { COPY(from_path, "settings_pane_highlights_options_bookmarks") }
home-prefs-snippets-header =
.label = { COPY(from_path, "settings_pane_snippets_header") }
home-prefs-topsites-description = { COPY(from_path, "prefs_topsites_description") }
home-prefs-topsites-header =
.label = { COPY(from_path, "settings_pane_topsites_header") }
home-prefs-recommended-by-description = { COPY(from_path, "prefs_topstories_description2") }
home-prefs-recommended-by-learn-more = { COPY(from_path, "pocket_how_it_works") }
home-prefs-recommended-by-option-sponsored-stories =
.label = { COPY(from_path, "prefs_topstories_options_sponsored_label") }
""", from_path="browser/chrome/browser/activity-stream/newtab.properties"
)
)
ctx.add_transforms(
TARGET_FILE,
SOURCE_FILE,
[
FTL.Message(
id=FTL.Identifier("home-prefs-recommended-by-header"),
attributes=[
FTL.Attribute(
id=FTL.Identifier("label"),
value=REPLACE(
"browser/chrome/browser/activity-stream/newtab.properties",
"header_recommended_by",
{
"{provider}": VARIABLE_REFERENCE("provider")
}
)
)
]
),
FTL.Message(
id=FTL.Identifier("home-prefs-highlights-option-saved-to-pocket"),
attributes=[
FTL.Attribute(
id=FTL.Identifier("label"),
value=REPLACE(
"browser/chrome/browser/activity-stream/newtab.properties",
"prefs_highlights_options_pocket_label",
{
"Pocket": TERM_REFERENCE("pocket-brand-name")
},
)
)
]
),
FTL.Message(
id=FTL.Identifier("home-prefs-snippets-description"),
value=REPLACE(
"browser/chrome/browser/activity-stream/newtab.properties",
"prefs_snippets_description",
{
"Mozilla": TERM_REFERENCE("vendor-short-name"),
"Firefox": TERM_REFERENCE("brand-product-name")
},
)
),
FTL.Message(
id=FTL.Identifier("home-prefs-sections-rows-option"),
attributes=[
FTL.Attribute(
id=FTL.Identifier("label"),
value=PLURALS(
"browser/chrome/browser/activity-stream/newtab.properties",
"prefs_section_rows_option",
VARIABLE_REFERENCE("num"),
lambda text: REPLACE_IN_TEXT(
text,
{
"{num}": VARIABLE_REFERENCE("num")
}
)
)
)
]
)
]
)