๐ ๋ ์ฌํ ํ: ์์ฉ ํ๋ก๊ทธ๋จ์ ๊ธฐ๋ณธ ๊ตญ์ ํ ์ถ๊ฐ(i18n)
15163 ๋จ์ด webdevsveltei18njavascript
๐ ์๋ ํ์ญ๋๊น?
Read this article en Espaรฑol(Chema์ ์์ด๋์ด๋ฅผ ํตํด ์ ํํ ๋งํ ๊ฐ์น๊ฐ ์์ต๋๋ค. ๊ฐ์ฌํฉ๋๋ค!)
๋๋ ์ต๊ทผ์ ์ฐ์ฐํ Matthias Stahl ๋ฐ์ฌcode here์ ๋ฉ์ง ์์์ ๋ฐ๊ฒฌํ๋ค. ๊ทธ๋ ๊ธฐ๋ณธ์ ์ธ i18n ๋ฒ์ญ์ ์์ ์์ฉ ํ๋ก๊ทธ๋จ์ ์ถ๊ฐํ ์ ์๋ ๊ฐ๋จํ ๋ฐฉ๋ฒ์ ์ ์ํ๋ค.
๋งํฐ์์ค ์คํ๋ฅด ๋ฐ์ฌ๐ช๐บ
@h_i_g_s_c_h
์ฃ์กํฉ๋๋ค. ์ ๋ ๋ฐฉ๊ธ ์ฝ 20์ค ์ฝ๋๋ก ์์ i18n ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๋ง๋ค์์ต๋๋ค.์ด ์ปดํ์ผ๋ฌ๋ ๋๋ฌด ๋ฏธ์ณค์ด!๐คทโโ๏ธ
2020๋ 12์ 30์ผ ์ค์ 10:05
๋๋ ์ฌ์ฐฝ์ค๊ณผ ๋์์ ์ฝ๊ฐ์ ์ต์ ํ์ ๊ฐํ๋ฅผ ์งํํ๋ ๊ฒ์ด ๋งค์ฐ ์ฌ๋ฏธ์๊ณ ์ ์ฉํ ๊ฒ์ด๋ผ๊ณ ์๊ฐํ๋ค.๐ค
์ฐ๋ฆฌ๋ ์ด๋ฐ ๊ฒ์ ์ฐฝ์กฐํ ๊ฒ์ด๋ค.
์ด ๊ฒ์๋ฌผ์ ๋๋ถ๋ถ ๊ณต๋ก๋ ๋งํฐ์์ค์ ๊ฒ์ด๊ธฐ ๋๋ฌธ์ ๋ฐ๋์ ๊ทธ๋ฅผ ์ดํด๋ณด๊ณ ๋ฏธํํด์ผ ํ๋ค!๐
๐ ์ฃผ์: ์ด๊ฒ์ i18next์ฒ๋ผ ๊ธฐ๋ฅ์ด ์๋น๋ ๊ตญ์ ํ ํด๊ฒฐ ๋ฐฉ์์ด ์๋๊ธฐ ๋๋ฌธ์ ์ด๊ฒ์ ๋น์ ์๊ฒ ์์ ํ ์ ํฉํ ํด๊ฒฐ ๋ฐฉ์์ด ์๋ ์๋ ์์ต๋๋ค!
๊ท์ฐฎ๋ค์ฒดํฌ ์์Svelte REPL with all the codeโ๏ธ
๋ฒ์ญ ๋์
Matthias์ ์์์, ๊ทธ๋ ๋งค์ฐ ๊น์ด ๋ฐํ ๋์์ ์ฌ์ฉํ์ฌ ๋ฌธ์์ด์ ์ ์ฅํ๋ค.์ด๊ฒ์ ๊ฐ๋ฅํ์ง๋ง, ๋์์ ์ฎ๊ฒจ๋ค๋ ์ผ ํ๊ธฐ ๋๋ฌธ์, ํนํ ๋ค์ค ํ๋ฌ๊ทธ์ธ ํค๊ฐ ์์ผ๋ฉด (์๊ฐํด ๋ด
app => page => section => component => label
.๋๋ ํ๋ฉด ๋์์ ์ ํํ๋๋ฐ ๊ทธ ํค๋ ๊ตญ์ ํ ์ธ์ด ํ๊ฒฝ์ ํ์ ํ๊ทธ(์๋ฅผ ๋ค์ด
en
์ ๋นen-US
์ ๋ณํ ๊ฐ์ ๋ํ๋ด๋ ์ ๊ตฌ๋ถ ์ด๋ฆ ๊ณต๊ฐ์ ๋ฌธ์์ด์ด๋ค.์ฐ๋ฆฌ๊ฐ ๋ง์ ๋ฒ์ญ์ ์ฒ๋ฆฌํ ๋, ์ด๊ฒ์ ์ฑ๋ฅ์ ์ฝ๊ฐ์ ์ด์ต์ด ์์ด์ผ ํ๋ค.๋ํ ํฌํจ๋ ๋ณ์์ HTML์ ๋ฒ์ญ ๋ฌธ์์ด์์ ์ง์๋ฉ๋๋ค.
// translations.js
export default {
en: {
"homepage.title": "Hello, World!",
"homepage.welcome": "Hi <strong>{{name}}</strong>, how are you?",
"homepage.time": "The current time is: {{time}}",
},
es: {
"homepage.title": "ยกHola Mundo!",
"homepage.welcome": "Hola, <strong>{{name}}</strong>, ยฟcรณmo estรกs?",
"homepage.time": "La hora actual es: {{time}}",
},
};
์ด๊ฒ์ ๋ฌธ์์ด, ์ซ์, ๋ ์ง ๋ฑ ๋ค์ํ ํ์๊ณผ ์ฃผ์
๊ฐ์ ์ง์ํ ์ ์๋๋ก ํฉ๋๋ค.๊ตฌ์ฑ ์์
์ฐ๋ฆฌ๋ ์ง๊ธ ์ฐ๋ฆฌ์ ๋ ์ฌํ ๊ตฌ์ฑ ์์๋ฅผ ๋ง๋ค ๊ฒ์ด๋ค huzzah!๐
์ด ๊ตฌ์ฑ ์์๋ ๋งค์ฐ ๊ฐ๋จํฉ๋๋ค. ์ฌ์ฉ์๊ฐ ์ฌ์ฉํ๊ณ ์ ํ๋ ์ธ์ด๋ฅผ ์ ํํ ์ ์๋ ๋๋กญ๋ค์ด ๋ชฉ๋ก์ ํฌํจํ๊ณ , HTML๊ณผ ์ฌ์ฉ์ ์ ์ ๋ณ์๊ฐ ์๋ ํ ์คํธ๋ฅผ ํฌํจํ๋ ๋ฒ์ญ ํ ์คํธ๋ฅผ ํ์ํฉ๋๋ค.
<!-- App.svelte -->
<script>
import { t, locale, locales } from "./i18n";
// Create a locale specific timestamp
$: time = new Date().toLocaleDateString($locale, {
weekday: "long",
year: "numeric",
month: "long",
day: "numeric",
});
</script>
<main>
<p>
<select bind:value={$locale}>
{#each locales as l}
<option value={l}>{l}</option>
{/each}
</select>
</p>
<h1>{$t("homepage.title")}!</h1>
<p>{@html $t("homepage.welcome", { name: "Jane Doe" })}!</p>
<p>{$t("homepage.time", { time })}!</p>
</main>
์ฐ๋ฆฌ๋ ๋ ์ฌํ ์ ์ฅ์์ <select>
์์๋ฅผ ์ฐ๊ฒฐํ๊ณ (์ฐ๋ฆฌ๋ 1์ด ์์ ๋ง๋ค ๊ฒ) ์ ๊ธฐํ $t()
๋ฐฉ๋ฒ์ผ๋ก ๋ฒ์ญ ๊ฒ์์ ํ๋ ๊ฒ์ด๋ค.๋ํ ์ฌ์ฉ์ ์ฌ์ฉ
toLocaleDateString
์ ํ์ํ๊ณ $locale
์ ์ฅ ๊ฐ์ ์ ๋ฌํ๋ ์ธ์ด ํ๊ฒฝ์ ๋ง๋ ์๊ฐ ์คํฌํ๋ฅผ ๋ง๋ค๊ณ ์์์ ์ ์ ์์ต๋๋ค.๋ง์ฝ ์ด๊ฒ์ด ์์ง ์๋ฏธ๊ฐ ์๋ค๋ฉด, ๊ด์ฐฎ์, ๊ณ์ ์ฝ์ด๋ผ!
์์ .
์ด์ ์ฐ๋ฆฌ ๋ ์ฌํ ์์ ์ ๋ง๋ค์!๐ฏโโ๏ธ
์ ์ฅ ์์ฒด๋ ๋งค์ฐ ๊ฐ๋จํ๋ค. ๊ธฐ๋ณธ์ ์ผ๋ก ์ฐ๋ฆฌ๋ ์ง์ญ ์ค์ ๊ฐ(์๋ฅผ ๋ค์ด
en
, es
๋ฑ)์ ํ ์ ์ฅ์์ ์ ์ฅํ ๋ค์์ ์ง์ญ ์ค์ ๊ณผ ์ฐ๋ฆฌ๊ฐ ์ด์ ์ ๋ง๋ translations ๋์์ ๋ฐ๋ผ derived
์ ์ฅ์ ๋ง๋ค ์ ์๋ค.import { derived, writable } from "svelte/store";
import translations from "./translations";
export const locale = writable("en");
export const locales = Object.keys(translations);
function translate(locale, key, vars) {
// Let's throw some errors if we're trying to use keys/locales that don't exist.
// We could improve this by using Typescript and/or fallback values.
if (!key) throw new Error("no key provided to $t()");
if (!locale) throw new Error(`no translation for key "${key}"`);
// Grab the translation from the translations object.
let text = translations[locale][key];
if (!text) throw new Error(`no translation found for ${locale}.${key}`);
// Replace any passed in variables in the translation string.
Object.keys(vars).map((k) => {
const regex = new RegExp(`{{${k}}}`, "g");
text = text.replace(regex, vars[k]);
});
return text;
}
export const t = derived(locale, ($locale) => (key, vars = {}) =>
translate($locale, key, vars)
);
๋๋ถ๋ถ์ ๋
ผ๋ฆฌ๋ translate
๋ฐฉ๋ฒ์์ ํค๋ฅผ ์ฐพ๊ณ ๋ณ์๋ฅผ ์ฃผ์
ํ๋ค(์กด์ฌํ๋ค๋ฉด).ํ์ ์ ์ฅ์๋ ํ์ฌ ์ธ์ด ํ๊ฒฝ๊ณผ ๋๊ธฐํ๋ ๊ฒ์ ๋๋ค. ๋ฐ๋ผ์ ์ฐ๋ฆฌ์
translate
๋ฐฉ๋ฒ์ ํธ์ถ๋ ๋ ํญ์ ํ์ฌ ์ธ์ด ํ๊ฒฝ์ ๋ฐ์ ๊ฒ์
๋๋ค.๋ก์ผ์ด ์
๋ฐ์ดํธ๋๋ฉด ํธ์ถ์ด ๋ค์ ๊ณ์ฐ๋์ด ์ฌ์ฉ์๊ฐ ๋ก์ผ์ ๋ณ๊ฒฝํ ๋ Svelte ๊ตฌ์ฑ ์์์ ๋ชจ๋ ํ
์คํธ๊ฐ ์
๋ฐ์ดํธ๋ฉ๋๋ค.์ฟจ!๐์ด๊ฒ์ ๋ฒ์ญ์ ์ํ ์ถ๊ฐ ์ ์ฅ์๋ฅผ ๋ง๋ค ํ์๊ฐ ์๊ธฐ ๋๋ฌธ์, ์ด๊ฒ์ ์๊ฒฉํ๊ฒ ํ์ํ ๊ฒ์ด ์๋๋ฉฐ, ์๋ตํ๋ฉด ํจ์จ์ด ๋์ฑ ๋์์ง ๊ฒ์ด๋ค.
๊ทธ๊ฒ์ ํ๋ฐ ๋๋ค
์ด์ ์ฐ๋ฆฌ๋ ์ฐ๋ฆฌ์ ์์ ์ด ์๊ฒผ๋ค. ์ฐ๋ฆฌ๋ ๋ชจ๋ ๊ฒ์ ๊ฐ์ง๊ณ ๋ ์ฌํ ๊ธฐ๋ณธ ๊ตญ์ ํ ์์คํ ์ ๋ง๋ค์๋ค. ์ถํํ๋ค.๐
์ด ์ฝ๋์ ์คํ์ ๋ณด๋ ค๋ฉด Svelte REPL
๐ฐ ํ์ธต ๋
ํ์ฌ ์ด ์ต์ ์ ๋ชจ๋ ์ฌ๋์๊ฒ ์ ํฉํ์ง ์๋ค.๋ง์ฝ ๋ง์ ๋ฒ์ญ์ ํฌํจํ๋ ๋ํ, ๊ฑด์ฅํ๊ณ , ๋ด์ฉ์ด ๋ฐ์ง๋ ํ๋ก๊ทธ๋จ์ ๊ตฌ์ถํ๊ณ ์๋ค๋ฉด, Locize ์ i18next ์ ๊ฒฐํฉํด์ ์ฌ์ฉํ ๊ฒ์ ๊ณ ๋ คํ ์ ์์ต๋๋ค.๋น์ทํ ๋ฐฉ์์ผ๋ก ๊ทธ๋ค์ JS ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ Svelte์ ํตํฉํ ์ ์์ต๋๋ค.
์ ํฌ๋ HTML ๋ด์ฉ์ ์ ๋ฆฌํ์ง ์์๊ธฐ ๋๋ฌธ์ ๋ฒ์ญ ๋ฌธ์์ด์ ์ฌ์ฉ์๊ฐ ์ ๊ณตํ ๋ฐ์ดํฐ๋ฅผ ์ฃผ์ ํ๋ฉด ์ ๋ ฅ์ ์ ๋ฆฌํ๊ฑฐ๋ ์ ๊ฑฐํด์ผ ํฉ๋๋คXSS vulnerability!๐
์ด๋ฐ ๋ฐฉ๋ฒ์ ๋ ๋ค๋ฅธ ๋ฌธ์ ๋ ๋ถ์กฑํ ๋ฒ์ญ์ ๋ํด ์ง์ ํ ๋ฐํ ํ์๊ฐ ์๋ค๋ ๊ฒ์ด๋ค. (์ง๊ธ ์ฐ๋ฆฌ๋ ๋จ์ง ์ค๋ฅ๋ฅผ ๋์ก์ ๋ฟ, ์ด๊ฒ์ ๋น์ ์ด ์ํ๋ ๊ฒ์ด ์๋ ์๋ ์๋ค.)
์ฆ, ์์ ํ ๋ฒ์ญ ํ๋ซํผ์ด ํ์ ์๊ณ ์๋์ ์ผ๋ก ๊ธฐ๋ณธ์ ์ธ ๋ฌธ์์ด๋ง ๋ฒ์ญํ ์ ์์ ๋ ์ด๋ฐ ํด๊ฒฐ ๋ฐฉ์์ด ๋์์ด ๋๋ค๋ ๊ฒ์ด๋ค.
์ด ์๋ persisting the locale value in local storage๋ก ํ์ฅํ ์ ์์ผ๋ฉฐ
$t()
property ๋ฑ์ ํตํด ๋ธ๋ผ์ฐ์ ์ ๊ธฐ๋ณธ ์ธ์ด๋ก ๊ธฐ๋ณธ๊ฐ์ผ๋ก ์ฌ์ฉํ ์ ์์ต๋๋ค.๊ทธ ์์ฒด๊ฐ ํ๋์ ์ฃผ์ !๐ฌ ์ง๋๋ฌ๋ฏธ
์ค์๊ฐ ํธ์ง ํ๊ฒฝ์์ Svelte REPL ์ ๋ชจ๋ ์ฝ๋๋ฅผ ๋ณด์ญ์์ค!๐ค
๋๋ ์ด ์๊ฐ ์ฐ๋ฆฌ์๊ฒ ๋ ์ฌํ ๋ช ๊ฐ์ง ์ฌ๋ฏธ์๋ ํน์ฑ์ ๋ณด์ฌ ์ฃผ์๋ค๊ณ ์๊ฐํ๋ค. ์๋ฅผ ๋ค์ด ๋ค์๊ณผ ๊ฐ๋ค.
1๏ธโฃ ์ด๋ป๊ฒ ์์ฃผ ์ ์ ์ฝ๋๋ก ๊ธฐ๋ฅ์ฑ์ ์คํํ์ง๋ง ๊ธฐ๋ณธ์ ์ธ i18n์ผ๋ก ์คํํฉ๋๊น
2๏ธโฃ ๋ฐํ ํจ์
navigator.languages
์ ์ฅ์๋ฅผ ์ด๋ป๊ฒ ์ฌ์ฉํฉ๋๊น์ ๏ธโฃ ๊ธ๋ก๋ฒ ์คํ ๋ฆฌ์ง๋ฅผ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ ๋ฐ ๊ตฌ์ฑ ์์์ ์ด๋ฌํ ๊ฐ์ ์ค์ ํ๋ ๋ฐฉ๋ฒ
4๏ธโฃ ๋ก์ผ๋ณ ๋ ์ง ํ์์ ๊ฐ์ ธ์ค๋ ๋ฐฉ๋ฒ
derived
๋น์ ์๊ฒ ์์ฃผ ์ฌ๋ฏธ์์์ผ๋ฉด ์ข๊ฒ ์ต๋๋ค. ๋งํฐ์์ค์ ์ค๋ฆฌ์ง๋ ๋๊ธ์ ์ํด ์๋ฆฌ ์ง๋ฅด๋ ๊ฒ์ ์์ง ๋ง์ธ์!์ฝ์ด์ฃผ์ ์ ๊ฐ์ฌํฉ๋๋ค!๊ณ ๋ฏผ ์ข ํด๋ณผ๊ฒ์. ์ด ๋๊ธ ํ๋๋ง ์ฃผ์ธ์.โค๏ธ, ๐ฆ ํน์๐ ๋์ค์ ์ฑ ๊ฐํผ๋ฅผ ์ถ๊ฐํฉ๋๋ค.๐
๋ค๋ฅธ ์ ์, ์์ด๋์ด, ํผ๋๋ฐฑ ๋๋ ์์ ์ฌํญ์ด ์์ต๋๊น?๋๊ธ๋ก ์๋ ค์ฃผ์ธ์!๐โโ๏ธ
Dev.to(), ํธ์ํฐ(),/๋๋ Githubdanawoodman์์ ๋๋ฅผ ์ฃผ๋ชฉํ๋ ๊ฒ์ ์์ง ๋ง๋ผ!
์ฌ์ง ์์ฑ์Joshua Aragon๊ฐ Unsplash
Reference
์ด ๋ฌธ์ ์ ๊ดํ์ฌ(๐ ๋ ์ฌํ ํ: ์์ฉ ํ๋ก๊ทธ๋จ์ ๊ธฐ๋ณธ ๊ตญ์ ํ ์ถ๊ฐ(i18n)), ์ฐ๋ฆฌ๋ ์ด๊ณณ์์ ๋ ๋ง์ ์๋ฃ๋ฅผ ๋ฐ๊ฒฌํ๊ณ ๋งํฌ๋ฅผ ํด๋ฆญํ์ฌ ๋ณด์๋ค https://dev.to/danawoodman/svelte-quick-tip-adding-basic-internationalization-i18n-to-you-app-2lmํ ์คํธ๋ฅผ ์์ ๋กญ๊ฒ ๊ณต์ ํ๊ฑฐ๋ ๋ณต์ฌํ ์ ์์ต๋๋ค.ํ์ง๋ง ์ด ๋ฌธ์์ URL์ ์ฐธ์กฐ URL๋ก ๋จ๊ฒจ ๋์ญ์์ค.
์ฐ์ํ ๊ฐ๋ฐ์ ์ฝํ ์ธ ๋ฐ๊ฒฌ์ ์ ๋ (Collection and Share based on the CC Protocol.)
์ข์ ์นํ์ด์ง ์ฆ๊ฒจ์ฐพ๊ธฐ
๊ฐ๋ฐ์ ์ฐ์ ์ฌ์ดํธ ์์ง
๊ฐ๋ฐ์๊ฐ ์์์ผ ํ ํ์ ์ฌ์ดํธ 100์ ์ถ์ฒ ์ฐ๋ฆฌ๋ ๋น์ ์ ์ํด 100๊ฐ์ ์์ฃผ ์ฌ์ฉํ๋ ๊ฐ๋ฐ์ ํ์ต ์ฌ์ดํธ๋ฅผ ์ ๋ฆฌํ์ต๋๋ค