Petar.petkovic has uploaded a new change for review. (
https://gerrit.wikimedia.org/r/372537 )
Change subject: Add article languages count
......................................................................
Add article languages count
Add languages count while searching and selecting
Add missing in target language information
Bug:T111094
Change-Id: I8fffe5b2056b977f1524b1978789266c281239d9
---
M extension.json
M i18n/api/en.json
M i18n/api/qqq.json
M i18n/en.json
M i18n/qqq.json
M modules/source/ext.cx.source.selector.js
M modules/source/styles/ext.cx.source.selector.less
M modules/ui/widgets/mw.cx.ui.PageSelectorWidget.js
8 files changed, 224 insertions(+), 12 deletions(-)
git pull
ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/ContentTranslation
refs/changes/37/372537/1
diff --git a/extension.json b/extension.json
index e1dc515..6a9ff0c 100644
--- a/extension.json
+++ b/extension.json
@@ -56,6 +56,9 @@
"cxpublishedtranslations": "ApiQueryPublishedTranslations",
"cxtranslatorstats": "ApiQueryTranslatorStats"
},
+ "APIPropModules": {
+ "langlinkscount": "ApiQueryLangLinksCount"
+ },
"MessagesDirs": {
"ContentTranslation": "i18n",
"ContentTranslationApi": "i18n/api"
@@ -76,6 +79,7 @@
"ApiQueryContentTranslationLanguageTrend":
"api/ApiQueryContentTranslationLanguageTrend.php",
"ApiQueryContentTranslationStats":
"api/ApiQueryContentTranslationStats.php",
"ApiQueryContentTranslationSuggestions":
"api/ApiQueryContentTranslationSuggestions.php",
+ "ApiQueryLangLinksCount": "api/ApiQueryLangLinksCount.php",
"ApiQueryPublishedTranslations":
"api/ApiQueryPublishedTranslations.php",
"ApiQueryTranslatorStats": "api/ApiQueryTranslatorStats.php",
"ContentTranslationHooks": "ContentTranslation.hooks.php",
@@ -2050,8 +2054,23 @@
],
"dependencies": [
"mediawiki.widgets",
+ "mw.cx.ui.TitleOptionWidget",
"oojs-ui.styles.icons-interactions"
]
+ },
+ "mw.cx.ui.TitleOptionWidget": {
+ "scripts": [
+ "ui/widgets/mw.cx.ui.TitleOptionWidget.js"
+ ],
+ "styles": [
+
"ui/styles/widgets/mw.cx.ui.TitleOptionWidget.less"
+ ],
+ "messages": [
+ "cx-sourceselector-missing-in-target-language"
+ ],
+ "dependencies": [
+ "mediawiki.widgets"
+ ]
}
},
"ResourceFileModulePaths": {
diff --git a/i18n/api/en.json b/i18n/api/en.json
index dd7f545..ebf9983 100644
--- a/i18n/api/en.json
+++ b/i18n/api/en.json
@@ -116,5 +116,8 @@
"apierror-cx-mustbeloggedin-viewtranslations": "To view your
translations, you must log in.",
"apierror-cx-samelanguages": "Source and target languages cannot be the
same.",
"apierror-cx-suggestionsdisabled": "Suggestions not enabled for this
wiki.",
- "apierror-cx-translationnotfound": "Translation not found."
+ "apierror-cx-translationnotfound": "Translation not found.",
+ "apihelp-query+langlinkscount-description": "Get the total number of
languages that article exists in.",
+ "apihelp-query+langlinkscount-summary": "Get the number of langlinks.",
+ "apihelp-query+langlinkscount-example-1": "Get number of langlinks for
'Dog' article"
}
diff --git a/i18n/api/qqq.json b/i18n/api/qqq.json
index a095870..f9bcd7b 100644
--- a/i18n/api/qqq.json
+++ b/i18n/api/qqq.json
@@ -113,5 +113,8 @@
"apierror-cx-mustbeloggedin-viewtranslations": "{{doc-apierror}}",
"apierror-cx-samelanguages": "{{doc-apierror}}",
"apierror-cx-suggestionsdisabled": "{{doc-apierror}}",
- "apierror-cx-translationnotfound": "{{doc-apierror}}"
+ "apierror-cx-translationnotfound": "{{doc-apierror}}",
+ "apihelp-query+langlinkscount-description":
"{{doc-apihelp-description|query+langlinkscount}}",
+ "apihelp-query+langlinkscount-summary":
"{{doc-apihelp-summary|query+langlinkscount}}",
+ "apihelp-query+langlinkscount-example-1":
"{{doc-apihelp-example|query+langlinkscount}}"
}
diff --git a/i18n/en.json b/i18n/en.json
index 6e39db4..449d4af 100644
--- a/i18n/en.json
+++ b/i18n/en.json
@@ -97,6 +97,7 @@
"cx-sourceselector-dialog-error-page-exists": "The page [$1 already
exists] in $2",
"cx-sourceselector-dialog-error-title-in-use": "The title for the new
page is [$1 already in use]",
"cx-sourceselector-dialog-error-no-source-article": "The page to
translate does not exist in $1",
+ "cx-sourceselector-missing-in-target-language": "Missing in $1",
"cx-mt-abuse-warning-title": "Your translation {{PLURAL:$1|contains}}
$1% of unmodified machine-translated text",
"cx-mt-abuse-warning-text": "Machine translation is provided only as a
starting point. You need to make sure that the content is accurate and reads
naturally in your language.",
"cx-publish-captcha-title": "Security question",
diff --git a/i18n/qqq.json b/i18n/qqq.json
index 7f0a630..7f849b1 100644
--- a/i18n/qqq.json
+++ b/i18n/qqq.json
@@ -106,6 +106,7 @@
"cx-sourceselector-dialog-error-page-exists": "An error message that
indicates there is a page in the target wiki with the same title as the
proposed source title.\n\nParameters:\n* $1 - link to existing target page.\n*
$2 - target language name.",
"cx-sourceselector-dialog-error-title-in-use": "Error that indicates
there is already a page in the target wiki with the same title as the proposed
target title.\n\nParameters:\n* $1 - link to target page with same title",
"cx-sourceselector-dialog-error-no-source-article": "Error that
indicates there is no page with the specified title in the source language to
translate.\n\nParameters:\n* $1 - the source language",
+ "cx-sourceselector-missing-in-target-language": "Label appended to
search result indicating that matching article is missing in target language",
"cx-mt-abuse-warning-title": "Title text shown in machine translation
abuse card.\n* $1: Percentage of machine translation",
"cx-mt-abuse-warning-text": "Detailed explanation of machine
translation abuse.",
"cx-publish-captcha-title": "Title of captcha form while publishing the
translation",
diff --git a/modules/source/ext.cx.source.selector.js
b/modules/source/ext.cx.source.selector.js
index 6bd6c62..0ba5c74 100644
--- a/modules/source/ext.cx.source.selector.js
+++ b/modules/source/ext.cx.source.selector.js
@@ -32,6 +32,7 @@
this.sourceLanguage = null;
this.targetLanguage = null;
this.$container = this.embedded ? options.container : null;
+ this.sourceTitles = null;
this.$selectedItem = null;
this.$selectedItemImage = null;
this.$selectedItemInfo = null;
@@ -748,14 +749,15 @@
}
this.$selectedItemInfo.append(
- $( '<a>' )
- .prop( {
- href: item.$label.prop( 'href' ),
- title: item.$label.prop( 'title' ),
- target: '_blank',
- text: item.$label.text()
- } )
- .click( this.openLinkInNewTab )
+ $( '<a>' ).prop( {
+ href: item.$label.prop( 'href' ),
+ title: item.$label.prop( 'title' ),
+ target: '_blank',
+ text: item.$label.text()
+ } ),
+ $( '<span>' )
+ .addClass(
'cx-sourceselector-embedded-selected-item__language-count' )
+ .text( item.initialConfig.numOfLanguages )
);
this.$container
.toggleClass( 'cx-sourceselector-embedded--selected' );
@@ -1005,7 +1007,8 @@
siteMapper: this.siteMapper,
value: this.options.sourceTitle,
validateTitle: true,
- placeholder: mw.msg(
'cx-sourceselector-dialog-source-title-placeholder' )
+ placeholder: mw.msg(
'cx-sourceselector-dialog-source-title-placeholder' ),
+ showRedirectTargets: true
} );
this.sourcePageSelector.onLookupMenuItemChoose =
this.setSelectedItem.bind( this );
diff --git a/modules/source/styles/ext.cx.source.selector.less
b/modules/source/styles/ext.cx.source.selector.less
index cbef4c5..6d0221a 100644
--- a/modules/source/styles/ext.cx.source.selector.less
+++ b/modules/source/styles/ext.cx.source.selector.less
@@ -94,12 +94,23 @@
padding-left: 0.7em;
font-size: 1.2em;
- font-weight: bold;
// Needs to be overridden for specificity
> a {
color: @colorGray1;
+ font-weight: bold;
}
+ }
+
+ &__language-count {
+ display: block;
+
+ .background-image-svg('../images/languages.svg',
'../images/languages.png');
+ background-size: 24px;
+ background-repeat: no-repeat;
+ background-position: left center;
+ padding-left: 26px;
+ font-size: 0.8em;
}
.cx-sourceselector-embedded-discard {
@@ -145,6 +156,8 @@
.flex-display( box ); // Support 2009 syntax
.flex-display();
+ -ms-flex-item-align: center; // Support IE10+
+ align-self: center;
margin-left: auto;
.cx-sourceselector-embedded__source-language,
diff --git a/modules/ui/widgets/mw.cx.ui.PageSelectorWidget.js
b/modules/ui/widgets/mw.cx.ui.PageSelectorWidget.js
index 6a7390c..4d6d840 100644
--- a/modules/ui/widgets/mw.cx.ui.PageSelectorWidget.js
+++ b/modules/ui/widgets/mw.cx.ui.PageSelectorWidget.js
@@ -69,6 +69,175 @@
optionWidgetData =
mw.widgets.TitleWidget.prototype.getOptionWidgetData.call( this, title, data );
// Correct the URL so that it can point to the source language wiki.
optionWidgetData.url = this.siteMapper.getPageUrl( this.language, title
);
+ optionWidgetData.numOfLanguages = data.langlinkscount;
+ optionWidgetData.missingInTargetLanguage = !data.langlinks;
+ optionWidgetData.targetLanguage = this.targetLanguage;
return optionWidgetData;
};
+
+mw.cx.ui.PageSelectorWidget.prototype.getSuggestionsPromise = function () {
+ var req,
+ api = this.getApi(),
+ query = this.getQueryValue(),
+ widget = this,
+ promiseAbortObject = { abort: function () {
+ // Do nothing. This is just so OOUI doesn't break due
to abort being undefined.
+ } };
+
+ // Set API URL to localhost for local testing of langlinks API
+ // api.apiUrl = this.api.defaults.ajax.url;
+ // api.defaults.ajax.url = this.api.defaults.ajax.url;
+
+ if ( mw.Title.newFromText( query ) ) {
+ return this.getInterwikiPrefixesPromise().then( function (
interwikiPrefixes ) {
+ var params,
+ interwiki = query.substring( 0, query.indexOf(
':' ) );
+ if (
+ interwiki && interwiki !== '' &&
+ interwikiPrefixes.indexOf( interwiki ) !== -1
+ ) {
+ return $.Deferred().resolve( { query: {
+ pages: [ {
+ title: query
+ } ]
+ } } ).promise( promiseAbortObject );
+ } else {
+ params = {
+ action: 'query',
+ prop: [ 'info', 'pageprops',
'langlinks', 'langlinkscount' ],
+ generator: 'prefixsearch',
+ gpssearch: query,
+ gpsnamespace: widget.namespace !== null
? widget.namespace : undefined,
+ gpslimit: widget.limit,
+ ppprop: 'disambiguation',
+ lllang: widget.targetLanguage
+ };
+ if ( widget.showRedirectTargets ) {
+ params.redirects = 1;
+ }
+ if ( widget.showImages ) {
+ params.prop.push( 'pageimages' );
+ params.pithumbsize = 80;
+ params.pilimit = widget.limit;
+ }
+ if ( widget.showDescriptions ) {
+ params.prop.push( 'pageterms' );
+ params.wbptterms = 'description';
+ }
+ req = api.get( params );
+ promiseAbortObject.abort = req.abort.bind( req
); // TODO ew
+ return req.then( function ( ret ) {
+ if ( ret.query === undefined ) {
+ ret = api.get( { action:
'query', titles: query } );
+ promiseAbortObject.abort =
ret.abort.bind( ret );
+ }
+ return ret;
+ } );
+ }
+ } ).promise( promiseAbortObject );
+ } else {
+ // Don't send invalid titles to the API.
+ // Just pretend it returned nothing so we can show the 'invalid
title' section
+ return $.Deferred().resolve( {} ).promise( promiseAbortObject );
+ }
+};
+
+mw.cx.ui.PageSelectorWidget.prototype.getOptionsFromData = function ( data ) {
+ var i, len, index, pageExists, pageExistsExact, suggestionPage, page,
redirect, redirects,
+ currentPageName = new mw.Title( mw.config.get(
'wgRelevantPageName' ) ).getPrefixedText(),
+ items = [],
+ titles = [],
+ titleObj = mw.Title.newFromText( this.getQueryValue() ),
+ redirectsTo = {},
+ pageData = {};
+
+ if ( data.redirects ) {
+ for ( i = 0, len = data.redirects.length; i < len; i++ ) {
+ redirect = data.redirects[ i ];
+ redirectsTo[ redirect.to ] = redirectsTo[ redirect.to ]
|| [];
+ redirectsTo[ redirect.to ].push( redirect.from );
+ }
+ }
+
+ for ( index in data.pages ) {
+ suggestionPage = data.pages[ index ];
+ // When excludeCurrentPage is set, don't list the current page
unless the user has type the full title
+ if ( this.excludeCurrentPage && suggestionPage.title ===
currentPageName && suggestionPage.title !== titleObj.getPrefixedText() ) {
+ continue;
+ }
+ pageData[ suggestionPage.title ] = {
+ known: suggestionPage.known !== undefined,
+ missing: suggestionPage.missing !== undefined,
+ redirect: suggestionPage.redirect !== undefined,
+ disambiguation: OO.getProp( suggestionPage,
'pageprops', 'disambiguation' ) !== undefined,
+ imageUrl: OO.getProp( suggestionPage, 'thumbnail',
'source' ),
+ description: OO.getProp( suggestionPage, 'terms',
'description' ),
+ langlinkscount: suggestionPage.langlinkscount,
+ langlinks: suggestionPage.langlinks,
+ // Sort index
+ index: suggestionPage.index
+ };
+
+ // Throw away pages from wrong namespaces. This can happen when
'showRedirectTargets' is true
+ // and we encounter a cross-namespace redirect.
+ if ( this.namespace === null || this.namespace ===
suggestionPage.ns ) {
+ titles.push( suggestionPage.title );
+ }
+
+ redirects = redirectsTo[ suggestionPage.title ] || [];
+ for ( i = 0, len = redirects.length; i < len; i++ ) {
+ pageData[ redirects[ i ] ] = {
+ missing: false,
+ known: true,
+ redirect: true,
+ disambiguation: false,
+ description: mw.msg(
'mw-widgets-titleinput-description-redirect', suggestionPage.title ),
+ langlinks: suggestionPage.langlinks,
+ // Sort index, just below its target
+ index: suggestionPage.index + 0.5
+ };
+ titles.push( redirects[ i ] );
+ }
+ }
+
+ titles.sort( function ( a, b ) {
+ return pageData[ a ].index - pageData[ b ].index;
+ } );
+
+ // If not found, run value through mw.Title to avoid treating a match
as a
+ // mismatch where normalisation would make them matching (T50476)
+
+ pageExistsExact = (
+ Object.prototype.hasOwnProperty.call( pageData,
this.getQueryValue() ) &&
+ (
+ !pageData[ this.getQueryValue() ].missing ||
+ pageData[ this.getQueryValue() ].known
+ )
+ );
+ pageExists = pageExistsExact || (
+ titleObj &&
+ Object.prototype.hasOwnProperty.call( pageData,
titleObj.getPrefixedText() ) &&
+ (
+ !pageData[ titleObj.getPrefixedText() ].missing ||
+ pageData[ titleObj.getPrefixedText() ].known
+ )
+ );
+
+ if ( this.cache ) {
+ this.cache.set( pageData );
+ }
+
+ // Offer the exact text as a suggestion if the page exists
+ if ( pageExists && !pageExistsExact ) {
+ titles.unshift( this.getQueryValue() );
+ pageData[ this.getQueryValue() ] = pageData[
titleObj.getPrefixedText() ];
+ }
+
+ for ( i = 0, len = titles.length; i < len; i++ ) {
+ page = pageData[ titles[ i ] ] || {};
+ items.push( new mw.cx.ui.TitleOptionWidget(
this.getOptionWidgetData( titles[ i ], page ) ) );
+ }
+
+ return items;
+};
--
To view, visit https://gerrit.wikimedia.org/r/372537
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I8fffe5b2056b977f1524b1978789266c281239d9
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/ContentTranslation
Gerrit-Branch: master
Gerrit-Owner: Petar.petkovic <[email protected]>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits