Completed Codes
English Blog Code - Changing Language
▶ English Post
<script>
setTimeout(() => {
if (document.querySelector('h1')) {
document.querySelector('h1').innerText = 'JIN-CO DEVLOG'
}
if (document.querySelector('em.txt_state')) {
document.querySelector('em.txt_state').innerText = 'Subscribe'
}
if (document.querySelector('.tags h2')) {
document.querySelector('.tags h2').innerText = 'Tags'
}
if (document.querySelector('.comment-form [name="name"]')) {
document.querySelector('.comment-form [name="name"]').placeholder = 'Name'
}
if (document.querySelector('.comment-form [name="password"]')) {
document.querySelector('.comment-form [name="password"]').placeholder = 'Password'
}
if (document.querySelector('.comment-form [name="comment"]')) {
document.querySelector('.comment-form [name="comment"]').placeholder = 'I appreciate your comment'
}
if (document.querySelector('.submit button')) {
document.querySelector('.submit button').innerText = 'Post'
}
if (document.querySelector('#footer .admin')) {
document.querySelector('#footer .admin').innerText = 'Admin'
}
if (document.querySelectorAll('.post-meta > span')[2]) {
document.querySelectorAll('.post-meta > span')[2].querySelector('a').innerText = 'Edit'
}
if (document.querySelectorAll('.post-meta > span')[3]) {
document.querySelectorAll('.post-meta > span')[3].querySelector('a').innerText = 'Delete'
}
if (document.querySelector(".uoc-icon span.txt_like.uoc-count")) {
document.querySelector(".uoc-icon span.txt_like.uoc-count").innerText = 'Like'
}
if (document.querySelectorAll('.tt_category .link_tit')) {
document.querySelectorAll('.tt_category .link_tit').innerText = 'All Category'
}
document.querySelector("head > title").innerText = document.querySelector("head > title").innerText.replace('웹 개발 이것저것', 'JIN-CO DEVLOG')
document.querySelector('html').lang = 'en'
}, 100);
</script>
<style>
.category_list>li:nth-of-type(even) {
display: inherit;
}
</style>
<style>
.category_list>li:nth-of-type(odd) {
display: none;
}
</style>
Common Style
▶ Skin Editor (CSS)
.category_list>li:nth-of-type(even) {
display: none;
}
Creating Buttons to Change Languages
▶ Korean Post
<style>
.lan-box {
display: flex;
height: 50px;
width: 180px;
gap: 5px;
margin-top: 10px;
margin-left: auto;
z-index: 15;
}
.lan-box a {
width: 50px;
flex-grow: 1;
display: flex;
align-items: center;
justify-content: center;
border: none;
cursor: pointer;
border-radius: 5px;
box-shadow: 1px 1px 1px 0 rgba(0, 0, 0, .3);
}
.lan-box a:active {
transform: scale(.97);
}
.lan-box a.disabled {
opacity: .2;
}
.lan-box a.disabled:active {
transform: scale(1);
}
.lan-box a {
text-decoration: none;
}
.lan-box button {
color: #fff;
}
</style>
<script>
const multiLanguageBox = document.createElement('div')
multiLanguageBox.className = 'lan-box'
multiLanguageBox.innerHTML = `
<a class="lan-kr-btn disabled" style="background-color: rgb(203, 37, 37, .8);">
<button>한글</button>
</a>
<a href="https://jin-co.tistory.com/190" class="lan-en-btn" style="background-color: rgb(61, 61, 233, .8);">
<button>EN</button>
</a>
`
if (window.location.href.includes('com/m/')) {
multiLanguageBox.style.top = '180px'
document.querySelector("#mainContent > div.blogview_tit").appendChild(multiLanguageBox)
} else {
document.querySelector('#content > div > div.hgroup').appendChild(multiLanguageBox)
}
</script>
▶ English Post
<style>
.lan-box {
display: flex;
height: 50px;
width: 180px;
gap: 5px;
margin-top: 10px;
margin-left: auto;
z-index: 15;
}
.lan-box a {
width: 50px;
flex-grow: 1;
display: flex;
align-items: center;
justify-content: center;
border: none;
cursor: pointer;
border-radius: 5px;
box-shadow: 1px 1px 1px 0 rgba(0, 0, 0, .3);
}
.lan-box a:active {
transform: scale(.97);
}
.lan-box a.disabled {
opacity: .2;
}
.lan-box a.disabled:active {
transform: scale(1);
}
.lan-box a {
text-decoration: none;
}
.lan-box button {
color: #fff;
}
</style>
<script>
const multiLanguageBox = document.createElement('div')
multiLanguageBox.className = 'lan-box'
multiLanguageBox.innerHTML = `
<a href="https://jin-co.tistory.com/190" class="lan-kr-btn" style="background-color: rgb(203, 37, 37, .8);">
<button>한글</button>
</a>
<a class="lan-en-btn disabled" style="background-color: rgb(61, 61, 233, .8);">
<button>EN</button>
</a>
`
if (window.location.href.includes('com/m/')) {
multiLanguageBox.style.top = '180px'
document.querySelector("#mainContent > div.blogview_tit").appendChild(multiLanguageBox)
} else {
document.querySelector('#content > div > div.hgroup').appendChild(multiLanguageBox)
}
</script>
※ Note that the address for each post should be changed to a matching pair.
Background
When I started blogging, I made two separate blogs with separate platforms for English (Medium) and Korean (tstory). But as the number of posts increased I started to feel to managing two separate platforms was overwhelming mainly because I had to create a separate format for each platform as they were not fully compatible and some features from one platform do even exist on the other.
So I decided to create one multilingual blog to minimize the workload and went through some of the possible options. As both of the platforms do not support a multilingual function and the Korean blog platform has more features and easy to manage, I decided to move what is on Medium to tstory.
Given the capabilities of tstory, I figured I could either create a separate blog or create two posts with an option to change languages on one blog.
Since having a centralized blog with lots of posts has a better chance to boost website traffic, I decided to go with the latter.
Analyzing
First, I scanned the UI to identify all the elements with a text. Based on the skin, Poster, that I was using I figured that I need to change the titles (the tap and the main)
the footer,
and finally menus
Implementation
I started creating categories for English posts first. I structured the categories in pairs adding the English version right after the Korean version counterpart as It would give me the exact comparison between the two leading better management.
For editing the code, I decided to use an 'HTML block' feature which tstory provides to allow users to embed personal HTML, CSS, or external script.
▶ Editing the Texts
Here is the list of texts that need a translation.
document.querySelector('h1')
document.querySelector('em.txt_state')
document.querySelector('.tags h2')
document.querySelector('.comment-form [name="name"]')
document.querySelector('.comment-form [name="password"]')
document.querySelector('.comment-form [name="comment"]')
document.querySelector('.submit button')
document.querySelector(".uoc-icon span.txt_like.uoc-count")
document.querySelector('#footer .admin')
document.querySelectorAll('.tt_category .link_tit')
// tap title
document.querySelector("head > title")
// admin
document.querySelectorAll('.post-meta > span')[2].querySelector('a')
document.querySelectorAll('.post-meta > span')[3].querySelector('a')
Even though the admin menu is only visible to me, I decided to change that as well for perfection (or just for fun lol). Changing the text part is easy.
<script>
document.querySelector('h1').innerText = 'JIN-CO BLOG'
document.querySelector('em.txt_state').innerText = 'Subscribe'
document.querySelector('.tags h2').innerText = 'Tags'
document.querySelector('.comment-form [name="name"]').placeholder = 'Name'
document.querySelector('.comment-form [name="password"]').placeholder = 'Password'
document.querySelector('.comment-form [name="comment"]').placeholder = 'I appreciate your comment'
document.querySelector('.submit button').innerText = 'Post'
document.querySelector('#footer .admin').innerText = 'Admin'
document.querySelectorAll('.post-meta > span')[2].querySelector('a').innerText = 'Edit'
document.querySelectorAll('.post-meta > span')[3].querySelector('a').innerText = 'Delete'
document.querySelector(".uoc-icon span.txt_like.uoc-count").innerText = 'Like'
document.querySelectorAll('.tt_category .link_tit').innerText = 'All Category'
document.querySelector("head > title").innerText = document.querySelector("head > title").innerText.replace('웹 개발 이것저것', 'JIN-CO DEVLOG')
</script>
But the problem is that when I call elements that do not exist, it throws an error and codes that come after won't even run.
An easy way to fix this is to add an 'if' statement to check if the element exists first before running any logic.
<script>
if(document.querySelector('h1')) {
document.querySelector('h1').innerText = 'JIN-CO BLOG'
}
</script>
After fixing the error, I gave it a try and realized that the text of some of the elements does not change. From personal experiences coming from working with this kind of situation, I assumed that the problem is coming from the completion of DOM. The best and easy way, I know, to solve this is to use 'setTimeout' to wait until all the elements are ready.
<script>
setTimeout(() => {
if(document.querySelector('h1')) {
document.querySelector('h1').innerText = 'JIN-CO BLOG'
}
if(document.querySelector('em.txt_state')) {
document.querySelector('em.txt_state').innerText = 'Subscribe'
}
if(document.querySelector('.tags h2')) {
document.querySelector('.tags h2').innerText = 'Tags'
}
if(document.querySelector('.comment-form [name="name"]')) {
document.querySelector('.comment-form [name="name"]').placeholder = 'Name'
}
if(document.querySelector('.comment-form [name="password"]')) {
document.querySelector('.comment-form [name="password"]').placeholder = 'Password'
}
if(document.querySelector('.comment-form [name="comment"]')) {
document.querySelector('.comment-form [name="comment"]').placeholder = 'I appreciate your comment'
}
if(document.querySelector('.submit button')) {
document.querySelector('.submit button').innerText = 'Post'
}
if(document.querySelector('#footer .admin')) {
document.querySelector('#footer .admin').innerText = 'Admin'
}
if (document.querySelectorAll('.post-meta > span')[2]) {
document.querySelectorAll('.post-meta > span')[2].querySelector('a').innerText = 'Edit'
}
if (document.querySelectorAll('.post-meta > span')[3]) {
document.querySelectorAll('.post-meta > span')[3].querySelector('a').innerText = 'Delete'
}
if (document.querySelector(".uoc-icon span.txt_like.uoc-count")) {
document.querySelector(".uoc-icon span.txt_like.uoc-count").innerText = 'Like'
}
if (document.querySelectorAll('.tt_category .link_tit')) {
document.querySelectorAll('.tt_category .link_tit').innerText = 'All Category'
}
document.querySelector("head > title").innerText = document.querySelector("head > title").innerText.replace('웹 개발 이것저것', 'JIN-CO DEVLOG')
document.querySelector('html').lang = 'en'
}, 100)
</script>
And I inserted the code using an 'HTML block at the bottom of the test post and ran
The result was successful!
▶ Editing Menus
Now, moving on to the menu part. If you recall, I organized the menu for one category in Korean and the English counterpart category right after it. So I thought using 'odd' and 'even' selectors would be a perfect solution for implementing a different style for each while maintaining the category structure.
.category_list > li:nth-of-type(even) {
display: none;
}
.category_list > li:nth-of-type(odd) {
display: none;
}
At that point, I already had way over 100 posts so editing each of the Korean posts was not an option. Instead, I used the 'skin editor' to implement a global style to hide the English menu and then switch the text to English when a user opens an English version.
Admin -> 'skin change'
Select CSS and insert the code
Now the English menus are gone (at least the items)
Now, let's add the changing text code in an English post using an 'HTML block'
<script>
if(document.querySelector('.tt_category .link_tit')) {
document.querySelector('.tt_category .link_tit').innerText = 'All Category'
}
</script>
<style>
.category_list > li:nth-of-type(even) {
display: inherit;
}
</style>
<style>
.category_list > li:nth-of-type(odd) {
display: none;
}
</style>
Well, it works fine!
Switching Languages
I figured having a language-switching option would be good. And using <a> tag seemed the easiest way.
▶ Korean Post
<style>
.lan-box {
display: flex;
height: 50px;
width: 180px;
gap: 5px;
margin-top: 10px;
margin-left: auto;
z-index: 15;
}
.lan-box a {
width: 50px;
flex-grow: 1;
display: flex;
align-items: center;
justify-content: center;
border: none;
cursor: pointer;
border-radius: 5px;
box-shadow: 1px 1px 1px 0 rgba(0, 0, 0, .3);
}
.lan-box a:active {
transform: scale(.97);
}
.lan-box a.disabled {
opacity: .2;
}
.lan-box a.disabled:active {
transform: scale(1);
}
.lan-box a {
text-decoration: none;
}
.lan-box button {
color: #fff;
}
</style>
<script>
const multiLanguageBox = document.createElement('div')
multiLanguageBox.className = 'lan-box'
multiLanguageBox.innerHTML = `
<a class="lan-kr-btn disabled" style="background-color: rgb(203, 37, 37, .8);">
<button>한글</button>
</a>
<a href="https://jin-co.tistory.com/190" class="lan-en-btn" style="background-color: rgb(61, 61, 233, .8);">
<button>EN</button>
</a>
`
if (window.location.href.includes('com/m/')) {
multiLanguageBox.style.top = '180px'
document.querySelector("#mainContent > div.blogview_tit").appendChild(multiLanguageBox)
} else {
document.querySelector('#content > div > div.hgroup').appendChild(multiLanguageBox)
}
</script>
▶ English Post
<style>
.lan-box {
display: flex;
height: 50px;
width: 180px;
gap: 5px;
margin-top: 10px;
margin-left: auto;
z-index: 15;
}
.lan-box a {
width: 50px;
flex-grow: 1;
display: flex;
align-items: center;
justify-content: center;
border: none;
cursor: pointer;
border-radius: 5px;
box-shadow: 1px 1px 1px 0 rgba(0, 0, 0, .3);
}
.lan-box a:active {
transform: scale(.97);
}
.lan-box a.disabled {
opacity: .2;
}
.lan-box a.disabled:active {
transform: scale(1);
}
.lan-box a {
text-decoration: none;
}
.lan-box button {
color: #fff;
}
</style>
<script>
const multiLanguageBox = document.createElement('div')
multiLanguageBox.className = 'lan-box'
multiLanguageBox.innerHTML = `
<a href="https://jin-co.tistory.com/190" class="lan-kr-btn" style="background-color: rgb(203, 37, 37, .8);">
<button>한글</button>
</a>
<a class="lan-en-btn disabled" style="background-color: rgb(61, 61, 233, .8);">
<button>EN</button>
</a>
`
if (window.location.href.includes('com/m/')) {
multiLanguageBox.style.top = '180px'
document.querySelector("#mainContent > div.blogview_tit").appendChild(multiLanguageBox)
} else {
document.querySelector('#content > div > div.hgroup').appendChild(multiLanguageBox)
}
</script>
Likewise, we can add each code to each version of posts using an 'HTML block', then it is finally done!
※ Multi-Lingual Blog SEO
SEO (Multi-Lingual Blog Indexing)
After making multi-lingual blog on tstory. I was looking for a way to improve search result for each content when I came across on how to help Google to identify pages with the same content but with different languages. Implementation The implementation is
jin-co.tistory.com
※ Multi-Lingual Blog (Book Club Skin)
tstory - Multilingual Blog (Book Club Skin)
I have created a multi-lingual blog using 'Poster' skin before and I realized that the code needed a little adjustment if it were to be used for other skins. In this writing, we will see how we can create a multi-lingual blog using 'Book Club' skin Complet
jin-co.tistory.com
'Web Development Tips > TStory' 카테고리의 다른 글
tstory - Multi-Lingual Blog (Book Club Skin) (4) | 2023.04.29 |
---|---|
tstory - How to Add A Clipboard Function (8) | 2023.03.18 |
tstory - How to Use the Format (2) | 2023.03.08 |
Tstory - Smooth rendering (1) | 2023.03.04 |