Issue 305 broken link because of language fixes (#322)

* without having having to worry about site design.

Let me know if double having is intentional

* distinguish case of no translation and en lang

* prettier recommends

* distinguish case of no translation and en lang

* prettier recommends

* merge with latest origin/master changes

* typo

* link with language fixes

* do not show language dropdown if only one enabled

* check translation outside of LanguageDropDown.render
This commit is contained in:
Richard Zhang 2017-12-19 17:44:43 -08:00 committed by Joel Marcey
parent 4a3da1f30a
commit a5e963dba1
8 changed files with 177 additions and 175 deletions

View file

@ -8,6 +8,16 @@
const React = require('react'); const React = require('react');
class Footer extends React.Component { class Footer extends React.Component {
docUrl(doc, language) {
const baseUrl = this.props.config.baseUrl;
return baseUrl + 'docs/' + (language ? language + '/' : '') + doc;
}
pageUrl(doc, language) {
const baseUrl = this.props.config.baseUrl;
return baseUrl + (language ? language + '/' : '') + doc;
}
render() { render() {
const currentYear = new Date().getFullYear(); const currentYear = new Date().getFullYear();
return ( return (
@ -25,40 +35,19 @@ class Footer extends React.Component {
</a> </a>
<div> <div>
<h5>Docs</h5> <h5>Docs</h5>
<a <a href={this.docUrl('doc1.html', this.props.language)}>
href={
this.props.config.baseUrl +
'docs/' +
this.props.language +
'/doc1.html'
}>
Getting Started (or other categories) Getting Started (or other categories)
</a> </a>
<a <a href={this.docUrl('doc2.html', this.props.language)}>
href={
this.props.config.baseUrl +
'docs/' +
this.props.language +
'/doc2.html'
}>
Guides (or other categories) Guides (or other categories)
</a> </a>
<a <a href={this.docUrl('doc3.html', this.props.language)}>
href={
this.props.config.baseUrl +
'docs/' +
this.props.language +
'/doc3.html'
}>
API Reference (or other categories) API Reference (or other categories)
</a> </a>
</div> </div>
<div> <div>
<h5>Community</h5> <h5>Community</h5>
<a <a href={this.pageUrl('users.html', this.props.language)}>
href={
this.props.config.baseUrl + this.props.language + '/users.html'
}>
User Showcase User Showcase
</a> </a>
<a <a

View file

@ -19,11 +19,11 @@ function imgUrl(img) {
} }
function docUrl(doc, language) { function docUrl(doc, language) {
return siteConfig.baseUrl + 'docs/' + language + '/' + doc; return siteConfig.baseUrl + 'docs/' + (language ? language + '/' : '') + doc;
} }
function pageUrl(page, language) { function pageUrl(page, language) {
return siteConfig.baseUrl + language + '/' + page; return siteConfig.baseUrl + (language ? language + '/' : '') + page;
} }
class Button extends React.Component { class Button extends React.Component {
@ -73,7 +73,7 @@ const PromoSection = props => (
class HomeSplash extends React.Component { class HomeSplash extends React.Component {
render() { render() {
let language = this.props.language || 'en'; let language = this.props.language || '';
return ( return (
<SplashContainer> <SplashContainer>
<Logo img_src={imgUrl('docusaurus.svg')} /> <Logo img_src={imgUrl('docusaurus.svg')} />
@ -198,7 +198,7 @@ const Showcase = props => {
class Index extends React.Component { class Index extends React.Component {
render() { render() {
let language = this.props.language || 'en'; let language = this.props.language || '';
return ( return (
<div> <div>

View file

@ -38,6 +38,7 @@ class Doc extends React.Component {
// If internationalization is enabled, show Recruiting link instead of Edit Link. // If internationalization is enabled, show Recruiting link instead of Edit Link.
if ( if (
this.props.language &&
this.props.language != 'en' && this.props.language != 'en' &&
this.props.config.translationRecruitingLink this.props.config.translationRecruitingLink
) { ) {

View file

@ -11,16 +11,11 @@ const React = require('react');
const fs = require('fs'); const fs = require('fs');
const siteConfig = require(CWD + '/siteConfig.js'); const siteConfig = require(CWD + '/siteConfig.js');
const translation = require('../../server/translation.js'); const translation = require('../../server/translation.js');
const env = require('../../server/env.js');
const translate = require('../../server/translate.js').translate; const translate = require('../../server/translate.js').translate;
const setLanguage = require('../../server/translate.js').setLanguage; const setLanguage = require('../../server/translate.js').setLanguage;
const ENABLE_TRANSLATION = fs.existsSync(CWD + '/languages.js');
const ENABLE_VERSIONING = fs.existsSync(CWD + '/versions.json');
let versions;
if (ENABLE_VERSIONING) {
versions = require(CWD + '/versions.json');
}
const readMetadata = require('../../server/readMetadata.js'); const readMetadata = require('../../server/readMetadata.js');
readMetadata.generateMetadataDocs(); readMetadata.generateMetadataDocs();
const Metadata = require('../metadata.js'); const Metadata = require('../metadata.js');
@ -28,26 +23,20 @@ const Metadata = require('../metadata.js');
// language dropdown nav item for when translations are enabled // language dropdown nav item for when translations are enabled
class LanguageDropDown extends React.Component { class LanguageDropDown extends React.Component {
render() { render() {
const enabledLanguages = [];
let currentLanguage = 'English'; let currentLanguage = 'English';
setLanguage(this.props.language); setLanguage(this.props.language || 'en');
let helpTranslateString = translate( let helpTranslateString = translate(
'Help Translate|recruit community translators for your project' 'Help Translate|recruit community translators for your project'
); );
// add all enabled languages to dropdown // add all enabled languages to dropdown
translation['languages'].map(lang => { const enabledLanguages = env.translation
if (lang.tag == this.props.language) { .enabledLanguages()
currentLanguage = lang.name; .filter(lang => lang !== this.props.language)
} .map(lang => (
if (lang.tag == this.props.language) {
return;
}
enabledLanguages.push(
<li key={lang.tag}> <li key={lang.tag}>
<a href={siteConfig.baseUrl + lang.tag}>{lang.name}</a> <a href={siteConfig.baseUrl + lang.tag}>{lang.name}</a>
</li> </li>
); ));
});
// if no languages are enabled besides English, return null // if no languages are enabled besides English, return null
if (enabledLanguages.length < 1) { if (enabledLanguages.length < 1) {
return null; return null;
@ -116,27 +105,32 @@ class HeaderNav extends React.Component {
</li> </li>
); );
} else if (link.languages) { } else if (link.languages) {
if (
env.translation.enabled &&
env.translation.enabledLanguages().length > 1
) {
return ( return (
// return language dropdown
<LanguageDropDown <LanguageDropDown
baseUrl={this.props.baseUrl} baseUrl={this.props.baseUrl}
language={this.props.language} language={this.props.language}
key="languagedropdown" key="languagedropdown"
/> />
); );
} else {
return null;
}
} else if (link.doc) { } else if (link.doc) {
// set link to document with current page's language/version // set link to document with current page's language/version
let id; const langPart = env.translation.enabled
if (!ENABLE_VERSIONING || this.props.version === 'next') { ? (this.props.language || 'en') + '-'
id = this.props.language + '-' + link.doc; : '';
} else { const versionPart =
id = env.versioning.enabled && this.props.version !== 'next'
this.props.language + ? '-version-' +
'-version-' + (this.props.version || env.versioning.latestVersion) +
(this.props.version || versions[0]) + '-'
'-' + : '';
link.doc; const id = langPart + versionPart + link.doc;
}
if (!Metadata[id]) { if (!Metadata[id]) {
if (id != link.doc) { if (id != link.doc) {
throw new Error( throw new Error(
@ -153,9 +147,13 @@ class HeaderNav extends React.Component {
href = this.props.config.baseUrl + Metadata[id].permalink; href = this.props.config.baseUrl + Metadata[id].permalink;
} else if (link.page) { } else if (link.page) {
// set link to page with current page's language if appropriate // set link to page with current page's language if appropriate
const language = this.props.language || '';
if (fs.existsSync(CWD + '/pages/en/' + link.page + '.js')) { if (fs.existsSync(CWD + '/pages/en/' + link.page + '.js')) {
href = href =
siteConfig.baseUrl + this.props.language + '/' + link.page + '.html'; siteConfig.baseUrl +
(language ? language + '/' : '') +
link.page +
'.html';
} else { } else {
href = siteConfig.baseUrl + link.page + '.html'; href = siteConfig.baseUrl + link.page + '.html';
} }
@ -180,7 +178,7 @@ class HeaderNav extends React.Component {
render() { render() {
const versionsLink = const versionsLink =
this.props.baseUrl + this.props.baseUrl +
(ENABLE_TRANSLATION (env.translation.enabled
? this.props.language + '/versions.html' ? this.props.language + '/versions.html'
: 'versions.html'); : 'versions.html');
return ( return (
@ -198,9 +196,9 @@ class HeaderNav extends React.Component {
<h2 className="headerTitle">{this.props.title}</h2> <h2 className="headerTitle">{this.props.title}</h2>
)} )}
</a> </a>
{ENABLE_VERSIONING && ( {env.versioning.enabled && (
<a href={versionsLink}> <a href={versionsLink}>
<h3>{this.props.version || versions[0]}</h3> <h3>{this.props.version || env.versioning.latestVersion}</h3>
</a> </a>
)} )}
{this.renderResponsiveNav()} {this.renderResponsiveNav()}

66
lib/server/env.js Normal file
View file

@ -0,0 +1,66 @@
/**
* Copyright (c) 2017-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
const CWD = process.cwd();
const fs = require('fs-extra');
const path = require('path');
const join = path.join;
const languages_js = join(CWD, 'languages.js');
const versions_json = join(CWD, 'versions.json');
class Translation {
constructor() {
this.enabled = false;
this.languages = [
{
enabled: true,
name: 'English',
tag: 'en',
},
];
this._load();
}
enabledLanguages() {
return this.languages.filter(lang => lang.enabled);
}
_load() {
if (fs.existsSync(languages_js)) {
this.enabled = true;
this.languages = require(languages_js);
}
}
}
class Versioning {
constructor() {
this.enabled = false;
this.latestVersion = null;
this.versions = [];
this._load();
}
_load() {
if (fs.existsSync(versions_json)) {
this.enabled = true;
this.versions = JSON.parse(fs.readFileSync(versions_json, 'utf8'));
this.latestVersion = this.versions[0];
}
}
}
const env = {
translation: new Translation(),
versioning: new Versioning(),
};
module.exports = env;

View file

@ -20,30 +20,15 @@ function execute() {
const glob = require('glob'); const glob = require('glob');
const chalk = require('chalk'); const chalk = require('chalk');
const Site = require('../core/Site.js'); const Site = require('../core/Site.js');
const env = require('./env.js');
const siteConfig = require(CWD + '/siteConfig.js'); const siteConfig = require(CWD + '/siteConfig.js');
const translate = require('./translate.js'); const translate = require('./translate.js');
const versionFallback = require('./versionFallback.js');
const feed = require('./feed.js'); const feed = require('./feed.js');
const sitemap = require('./sitemap.js'); const sitemap = require('./sitemap.js');
const join = path.join; const join = path.join;
const ENABLE_TRANSLATION = fs.existsSync(join(CWD, 'languages.js'));
const ENABLE_VERSIONING = fs.existsSync(join(CWD, 'versions.json'));
let languages;
if (ENABLE_TRANSLATION) {
languages = require(CWD + '/languages.js');
} else {
languages = [
{
enabled: true,
name: 'English',
tag: 'en',
},
];
}
// create the folder path for a file if it does not exist, then write the file // create the folder path for a file if it does not exist, then write the file
function writeFileAndCreateFolder(file, content) { function writeFileAndCreateFolder(file, content) {
mkdirp.sync(file.replace(new RegExp('/[^/]*$'), '')); mkdirp.sync(file.replace(new RegExp('/[^/]*$'), ''));
@ -87,10 +72,9 @@ function execute() {
console.log('generate.js triggered...'); console.log('generate.js triggered...');
// array of tags of enabled languages // array of tags of enabled languages
const enabledLanguages = []; const enabledLanguages = env.translation
languages.filter(lang => lang.enabled).map(lang => { .enabledLanguages()
enabledLanguages.push(lang.tag); .map(lang => lang.tag);
});
readMetadata.generateMetadataDocs(); readMetadata.generateMetadataDocs();
const Metadata = require('../core/metadata.js'); const Metadata = require('../core/metadata.js');
@ -134,7 +118,7 @@ function execute() {
// determine what file to use according to its id // determine what file to use according to its id
let file; let file;
if (metadata.original_id) { if (metadata.original_id) {
if (ENABLE_TRANSLATION && metadata.language !== 'en') { if (env.translation.enabled && metadata.language !== 'en') {
file = join(CWD, 'translated_docs', metadata.language, metadata.source); file = join(CWD, 'translated_docs', metadata.language, metadata.source);
} else { } else {
file = join(CWD, 'versioned_docs', metadata.source); file = join(CWD, 'versioned_docs', metadata.source);
@ -161,12 +145,7 @@ function execute() {
rawContent = insertTableOfContents(rawContent); rawContent = insertTableOfContents(rawContent);
} }
let latestVersion; let latestVersion = env.versioning.latestVersion;
if (ENABLE_VERSIONING) {
latestVersion = JSON.parse(
fs.readFileSync(join(CWD, 'versions.json'), 'utf8')
)[0];
}
// replace any links to markdown files to their website html links // replace any links to markdown files to their website html links
Object.keys(mdToHtml).forEach(function(key, index) { Object.keys(mdToHtml).forEach(function(key, index) {
@ -207,7 +186,10 @@ function execute() {
writeFileAndCreateFolder(targetFile, str); writeFileAndCreateFolder(targetFile, str);
// generate english page redirects when languages are enabled // generate english page redirects when languages are enabled
if (ENABLE_TRANSLATION && metadata.permalink.indexOf('docs/en') !== -1) { if (
env.translation.enabled &&
metadata.permalink.indexOf('docs/en') !== -1
) {
const redirectComp = ( const redirectComp = (
<Redirect <Redirect
metadata={metadata} metadata={metadata}
@ -443,21 +425,26 @@ function execute() {
str str
); );
} }
} else {
// allow for rendering of other files not in pages/en folder // write to base level
let language = 'en'; let language = env.translation.enabled ? 'en' : '';
for (let i = 0; i < langParts.length; i++) {
if (enabledLanguages.indexOf(langParts[i]) !== -1) {
language = langParts[i];
}
}
translate.setLanguage(language); translate.setLanguage(language);
const str = renderToStaticMarkup( const str = renderToStaticMarkup(
<Site language={language} config={siteConfig}> <Site language={language} config={siteConfig}>
<ReactComp language={language} /> <ReactComp language={language} />
</Site> </Site>
); );
writeFileAndCreateFolder(targetFile, str); writeFileAndCreateFolder(targetFile.replace('/en/', '/'), str);
} else {
// allow for rendering of other files not in pages/en folder
let language = env.translation.enabled ? 'en' : '';
translate.setLanguage(language);
const str = renderToStaticMarkup(
<Site language={language} config={siteConfig}>
<ReactComp language={language} />
</Site>
);
writeFileAndCreateFolder(targetFile.replace('/en/', '/'), str);
} }
fs.removeSync(tempFile); fs.removeSync(tempFile);
} else if (!fs.lstatSync(file).isDirectory()) { } else if (!fs.lstatSync(file).isDirectory()) {
@ -469,15 +456,6 @@ function execute() {
} }
}); });
// copy html files in 'en' to base level as well
files = glob.sync(join(buildDir, 'en', '**'));
files.forEach(file => {
let targetFile = file.replace(join(buildDir, 'en'), join(buildDir));
if (file.match(/\.html$/)) {
fs.copySync(file, targetFile);
}
});
// Generate CNAME file if a custom domain is specified in siteConfig // Generate CNAME file if a custom domain is specified in siteConfig
if (siteConfig.cname) { if (siteConfig.cname) {
let targetFile = join(buildDir, 'CNAME'); let targetFile = join(buildDir, 'CNAME');

View file

@ -11,25 +11,12 @@ const path = require('path');
const fs = require('fs'); const fs = require('fs');
const glob = require('glob'); const glob = require('glob');
const chalk = require('chalk'); const chalk = require('chalk');
const env = require('./env');
const siteConfig = require(CWD + '/siteConfig.js'); const siteConfig = require(CWD + '/siteConfig.js');
const versionFallback = require('./versionFallback.js'); const versionFallback = require('./versionFallback.js');
const escapeStringRegexp = require('escape-string-regexp'); const escapeStringRegexp = require('escape-string-regexp');
const ENABLE_VERSIONING = fs.existsSync(CWD + '/versions.json');
let languages;
if (fs.existsSync(CWD + '/languages.js')) {
languages = require(CWD + '/languages.js');
} else {
languages = [
{
enabled: true,
name: 'English',
tag: 'en',
},
];
}
// Can have a custom docs path. Top level folder still needs to be in directory // Can have a custom docs path. Top level folder still needs to be in directory
// at the same level as `website`, not inside `website`. // at the same level as `website`, not inside `website`.
// e.g., docs/whereDocsReallyExist // e.g., docs/whereDocsReallyExist
@ -129,11 +116,8 @@ function processMetadata(file) {
'/' + escapeStringRegexp(getDocsPath()) + '/(.*)/.*/' '/' + escapeStringRegexp(getDocsPath()) + '/(.*)/.*/'
); );
let language = 'en';
const match = regexSubFolder.exec(file); const match = regexSubFolder.exec(file);
if (match) { let language = match ? match[1] : 'en';
language = match[1];
}
const metadata = result.metadata; const metadata = result.metadata;
const rawContent = result.rawContent; const rawContent = result.rawContent;
@ -149,28 +133,20 @@ function processMetadata(file) {
metadata.title = metadata.id; metadata.title = metadata.id;
} }
if (languages.length === 1 && !siteConfig.useEnglishUrl) { const langPart =
metadata.permalink = 'docs/' + metadata.id + '.html'; env.translation.enabled || siteConfig.useEnglishUrl ? language + '/' : '';
} else { let versionPart = '';
metadata.permalink = 'docs/' + language + '/' + metadata.id + '.html'; if (env.versioning.enabled) {
metadata.version = 'next';
versionPart = 'next/';
} }
if (ENABLE_VERSIONING) { metadata.permalink = 'docs/' + langPart + versionPart + metadata.id + '.html';
metadata.version = 'next';
if (languages.length === 1 && !siteConfig.useEnglishUrl) {
metadata.permalink = metadata.permalink.replace('docs/', 'docs/next/');
} else {
metadata.permalink = metadata.permalink.replace(
'docs/' + language + '/',
'docs/' + language + '/next/'
);
}
}
// change ids previous, next // change ids previous, next
metadata.localized_id = metadata.id; metadata.localized_id = metadata.id;
metadata.id = language + '-' + metadata.id; metadata.id = (env.translation.enabled ? language + '-' : '') + metadata.id;
metadata.language = language; metadata.language = env.translation.enabled ? language : 'en';
const order = readSidebar(); const order = readSidebar();
const id = metadata.localized_id; const id = metadata.localized_id;
@ -181,11 +157,13 @@ function processMetadata(file) {
if (order[id].next) { if (order[id].next) {
metadata.next_id = order[id].next; metadata.next_id = order[id].next;
metadata.next = language + '-' + order[id].next; metadata.next =
(env.translation.enabled ? language + '-' : '') + order[id].next;
} }
if (order[id].previous) { if (order[id].previous) {
metadata.previous_id = order[id].previous; metadata.previous_id = order[id].previous;
metadata.previous = language + '-' + order[id].previous; metadata.previous =
(env.translation.enabled ? language + '-' : '') + order[id].previous;
} }
} }
@ -204,10 +182,9 @@ function generateMetadataDocs() {
const regexSubFolder = /translated_docs\/(.*)\/.*/; const regexSubFolder = /translated_docs\/(.*)\/.*/;
const enabledLanguages = []; const enabledLanguages = env.translation
languages.filter(lang => lang.enabled).map(lang => { .enabledLanguages()
enabledLanguages.push(lang.tag); .map(language => language.tag);
});
const metadatas = {}; const metadatas = {};
const defaultMetadatas = {}; const defaultMetadatas = {};

View file

@ -9,6 +9,7 @@
function execute(port) { function execute(port) {
const extractTranslations = require('../write-translations.js'); const extractTranslations = require('../write-translations.js');
const env = require('./env.js');
const translation = require('./translation.js'); const translation = require('./translation.js');
const express = require('express'); const express = require('express');
const React = require('react'); const React = require('react');
@ -23,15 +24,12 @@ function execute(port) {
const glob = require('glob'); const glob = require('glob');
const chalk = require('chalk'); const chalk = require('chalk');
const translate = require('./translate.js'); const translate = require('./translate.js');
const versionFallback = require('./versionFallback');
const feed = require('./feed.js'); const feed = require('./feed.js');
const sitemap = require('./sitemap.js'); const sitemap = require('./sitemap.js');
// const sitemap = require("sitemap"); // const sitemap = require("sitemap");
const CWD = process.cwd(); const CWD = process.cwd();
const ENABLE_TRANSLATION = fs.existsSync(CWD + '/languages.js');
const ENABLE_VERSIONING = fs.existsSync(CWD + '/versions.json');
// remove a module and child modules from require cache, so server does not have // remove a module and child modules from require cache, so server does not have
// to be restarted // to be restarted
@ -168,14 +166,14 @@ function execute(port) {
// determine what file to use according to its id // determine what file to use according to its id
let file; let file;
if (metadata.original_id) { if (metadata.original_id) {
if (ENABLE_TRANSLATION && metadata.language !== 'en') { if (env.translation.enabled && metadata.language !== 'en') {
file = file =
CWD + '/translated_docs/' + metadata.language + '/' + metadata.source; CWD + '/translated_docs/' + metadata.language + '/' + metadata.source;
} else { } else {
file = CWD + '/versioned_docs/' + metadata.source; file = CWD + '/versioned_docs/' + metadata.source;
} }
} else { } else {
if (metadata.language === 'en') { if (!env.translation.enabled || metadata.language === 'en') {
file = file =
CWD + '/../' + readMetadata.getDocsPath() + '/' + metadata.source; CWD + '/../' + readMetadata.getDocsPath() + '/' + metadata.source;
} else { } else {
@ -197,12 +195,7 @@ function execute(port) {
rawContent = insertTableOfContents(rawContent); rawContent = insertTableOfContents(rawContent);
} }
let latestVersion; let latestVersion = env.latestVersion;
if (ENABLE_VERSIONING) {
latestVersion = JSON.parse(
fs.readFileSync(CWD + '/versions.json', 'utf8')
)[0];
}
// replace any links to markdown files to their website html links // replace any links to markdown files to their website html links
Object.keys(mdToHtml).forEach(function(key, index) { Object.keys(mdToHtml).forEach(function(key, index) {
@ -383,21 +376,21 @@ function execute(port) {
file = file.replace(siteConfig.baseUrl, ''); file = file.replace(siteConfig.baseUrl, '');
let userFile = CWD + '/pages/' + file; let userFile = CWD + '/pages/' + file;
let language = 'en'; let language = env.translation.enabled ? 'en' : '';
const regexLang = /(.*)\/.*\.html$/; const regexLang = /(.*)\/.*\.html$/;
const match = regexLang.exec(req.path); const match = regexLang.exec(req.path);
const parts = match[1].split('/'); const parts = match[1].split('/');
const enabledLangTags = []; const enabledLangTags = env.translation
for (let i = 0; i < translation['languages'].length; i++) { .enabledLanguages()
enabledLangTags.push(translation['languages'][i].tag); .map(lang => lang.tag);
}
for (let i = 0; i < parts.length; i++) { for (let i = 0; i < parts.length; i++) {
if (enabledLangTags.indexOf(parts[i]) !== -1) { if (enabledLangTags.indexOf(parts[i]) !== -1) {
language = parts[i]; language = parts[i];
} }
} }
let englishFile = CWD + '/pages/' + file; let englishFile = CWD + '/pages/' + file;
if (language !== 'en') { if (language && language !== 'en') {
englishFile = englishFile.replace('/' + language + '/', '/en/'); englishFile = englishFile.replace('/' + language + '/', '/en/');
} }